From 5099ac242fc32215c0b335a488a8bdbdd6829576 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Fabian=20Gr=C3=BCnbichler?= Date: Thu, 14 Jul 2022 13:04:27 +0200 Subject: [PATCH] New upstream version 1.60.0+dfsg1 --- Cargo.lock | 928 +-- RELEASES.md | 162 +- compiler/rustc/Cargo.toml | 6 +- compiler/rustc_arena/Cargo.toml | 1 - compiler/rustc_arena/src/lib.rs | 61 +- compiler/rustc_ast/src/ast.rs | 97 +- compiler/rustc_ast/src/ast_like.rs | 39 +- compiler/rustc_ast/src/attr/mod.rs | 13 +- compiler/rustc_ast/src/lib.rs | 2 +- compiler/rustc_ast/src/mut_visit.rs | 38 +- compiler/rustc_ast/src/ptr.rs | 8 +- compiler/rustc_ast/src/tokenstream.rs | 2 +- compiler/rustc_ast/src/util/comments.rs | 44 +- compiler/rustc_ast/src/util/comments/tests.rs | 34 +- compiler/rustc_ast/src/visit.rs | 28 +- compiler/rustc_ast_lowering/src/asm.rs | 21 +- compiler/rustc_ast_lowering/src/block.rs | 4 +- compiler/rustc_ast_lowering/src/expr.rs | 118 +- compiler/rustc_ast_lowering/src/index.rs | 11 +- compiler/rustc_ast_lowering/src/item.rs | 174 +- compiler/rustc_ast_lowering/src/lib.rs | 201 +- compiler/rustc_ast_lowering/src/pat.rs | 8 +- compiler/rustc_ast_lowering/src/path.rs | 18 +- compiler/rustc_ast_passes/Cargo.toml | 2 +- .../rustc_ast_passes/src/ast_validation.rs | 25 +- compiler/rustc_ast_passes/src/feature_gate.rs | 65 +- compiler/rustc_ast_passes/src/lib.rs | 1 + compiler/rustc_ast_passes/src/node_count.rs | 4 +- compiler/rustc_ast_pretty/Cargo.toml | 1 - compiler/rustc_ast_pretty/src/pp.rs | 498 +- .../rustc_ast_pretty/src/pp/convenience.rs | 94 + compiler/rustc_ast_pretty/src/pp/ring.rs | 77 + compiler/rustc_ast_pretty/src/pprust/state.rs | 1313 +--- .../src/pprust/state/delimited.rs | 41 + .../rustc_ast_pretty/src/pprust/state/expr.rs | 587 ++ .../rustc_ast_pretty/src/pprust/state/item.rs | 664 ++ compiler/rustc_attr/src/builtin.rs | 171 +- compiler/rustc_attr/src/lib.rs | 2 + compiler/rustc_borrowck/Cargo.toml | 2 +- .../rustc_borrowck/src/borrowck_errors.rs | 2 +- .../src/constraint_generation.rs | 4 +- .../rustc_borrowck/src/constraints/mod.rs | 2 +- compiler/rustc_borrowck/src/dataflow.rs | 9 - compiler/rustc_borrowck/src/def_use.rs | 3 - .../src/diagnostics/bound_region_errors.rs | 28 +- .../src/diagnostics/conflict_errors.rs | 69 +- .../rustc_borrowck/src/diagnostics/mod.rs | 172 +- .../src/diagnostics/move_errors.rs | 18 +- .../src/diagnostics/mutability_errors.rs | 123 +- .../src/diagnostics/outlives_suggestion.rs | 2 +- .../src/diagnostics/region_errors.rs | 65 +- .../src/diagnostics/region_name.rs | 146 +- compiler/rustc_borrowck/src/invalidation.rs | 51 +- compiler/rustc_borrowck/src/lib.rs | 270 +- compiler/rustc_borrowck/src/nll.rs | 11 +- .../src/region_infer/dump_mir.rs | 2 +- .../rustc_borrowck/src/region_infer/mod.rs | 6 +- .../src/region_infer/opaque_types.rs | 8 +- compiler/rustc_borrowck/src/renumber.rs | 8 +- .../src/type_check/constraint_conversion.rs | 8 +- .../src/type_check/free_region_relations.rs | 2 +- .../src/type_check/liveness/trace.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 43 +- .../src/type_check/relate_tys.rs | 2 +- .../rustc_borrowck/src/universal_regions.rs | 47 +- compiler/rustc_builtin_macros/src/asm.rs | 21 +- .../rustc_builtin_macros/src/concat_bytes.rs | 4 +- .../src/deriving/default.rs | 3 - .../rustc_builtin_macros/src/deriving/mod.rs | 2 +- compiler/rustc_builtin_macros/src/format.rs | 223 +- .../src/global_allocator.rs | 22 +- compiler/rustc_builtin_macros/src/lib.rs | 6 +- compiler/rustc_builtin_macros/src/llvm_asm.rs | 303 - .../src/proc_macro_harness.rs | 5 - .../src/standard_library_imports.rs | 3 +- compiler/rustc_builtin_macros/src/test.rs | 32 +- compiler/rustc_codegen_cranelift/Cargo.lock | 4 +- compiler/rustc_codegen_cranelift/Cargo.toml | 2 +- .../rustc_codegen_cranelift/src/abi/mod.rs | 2 +- .../rustc_codegen_cranelift/src/archive.rs | 2 - compiler/rustc_codegen_cranelift/src/base.rs | 22 +- .../rustc_codegen_cranelift/src/common.rs | 4 +- .../rustc_codegen_cranelift/src/constant.rs | 22 +- .../src/debuginfo/mod.rs | 6 +- .../rustc_codegen_cranelift/src/inline_asm.rs | 12 +- .../src/intrinsics/mod.rs | 2 +- .../rustc_codegen_cranelift/src/unsize.rs | 2 +- .../src/value_and_place.rs | 10 +- compiler/rustc_codegen_gcc/src/archive.rs | 3 - compiler/rustc_codegen_gcc/src/asm.rs | 19 +- compiler/rustc_codegen_gcc/src/builder.rs | 29 +- compiler/rustc_codegen_gcc/src/consts.rs | 2 +- compiler/rustc_codegen_gcc/src/type_of.rs | 2 +- compiler/rustc_codegen_llvm/src/abi.rs | 2 +- compiler/rustc_codegen_llvm/src/allocator.rs | 4 +- compiler/rustc_codegen_llvm/src/asm.rs | 120 +- compiler/rustc_codegen_llvm/src/attributes.rs | 53 +- .../rustc_codegen_llvm/src/back/archive.rs | 216 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 17 - compiler/rustc_codegen_llvm/src/builder.rs | 132 +- compiler/rustc_codegen_llvm/src/consts.rs | 9 + compiler/rustc_codegen_llvm/src/context.rs | 95 +- .../src/coverageinfo/mapgen.rs | 141 +- .../src/debuginfo/metadata.rs | 694 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 77 +- .../rustc_codegen_llvm/src/debuginfo/utils.rs | 54 +- compiler/rustc_codegen_llvm/src/declare.rs | 1 + compiler/rustc_codegen_llvm/src/intrinsic.rs | 44 +- compiler/rustc_codegen_llvm/src/lib.rs | 2 + compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 47 +- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 4 + compiler/rustc_codegen_llvm/src/llvm_util.rs | 104 +- compiler/rustc_codegen_llvm/src/type_of.rs | 6 +- compiler/rustc_codegen_ssa/Cargo.toml | 6 +- .../rustc_codegen_ssa/src/back/archive.rs | 1 - compiler/rustc_codegen_ssa/src/back/link.rs | 40 +- compiler/rustc_codegen_ssa/src/back/linker.rs | 154 +- .../rustc_codegen_ssa/src/back/metadata.rs | 26 +- .../src/back/symbol_export.rs | 2 +- compiler/rustc_codegen_ssa/src/back/write.rs | 8 +- compiler/rustc_codegen_ssa/src/base.rs | 4 +- .../src/debuginfo/type_names.rs | 152 +- compiler/rustc_codegen_ssa/src/lib.rs | 1 + compiler/rustc_codegen_ssa/src/mir/analyze.rs | 1 - compiler/rustc_codegen_ssa/src/mir/block.rs | 35 +- .../rustc_codegen_ssa/src/mir/constant.rs | 8 +- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 10 + compiler/rustc_codegen_ssa/src/mir/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 143 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 150 +- .../rustc_codegen_ssa/src/mir/statement.rs | 47 - .../rustc_codegen_ssa/src/target_features.rs | 15 +- compiler/rustc_codegen_ssa/src/traits/asm.rs | 10 - .../rustc_codegen_ssa/src/traits/builder.rs | 205 +- .../src/traits/coverageinfo.rs | 8 +- .../rustc_const_eval/src/const_eval/error.rs | 30 +- .../src/const_eval/eval_queries.rs | 25 +- .../src/const_eval/fn_queries.rs | 13 +- .../src/const_eval/machine.rs | 35 + .../rustc_const_eval/src/const_eval/mod.rs | 50 +- .../rustc_const_eval/src/interpret/cast.rs | 2 +- .../src/interpret/eval_context.rs | 20 +- .../src/interpret/intrinsics/type_name.rs | 2 +- .../rustc_const_eval/src/interpret/operand.rs | 12 +- .../rustc_const_eval/src/interpret/place.rs | 2 +- .../rustc_const_eval/src/interpret/step.rs | 2 - .../rustc_const_eval/src/interpret/util.rs | 22 +- .../src/interpret/validity.rs | 12 +- compiler/rustc_const_eval/src/lib.rs | 5 +- .../src/transform/check_consts/check.rs | 109 +- .../src/transform/check_consts/ops.rs | 434 +- .../src/transform/check_consts/qualifs.rs | 69 +- .../src/transform/promote_consts.rs | 24 +- .../src/transform/validate.rs | 19 +- .../rustc_const_eval/src/util/call_kind.rs | 143 + compiler/rustc_const_eval/src/util/mod.rs | 2 + compiler/rustc_data_structures/Cargo.toml | 6 +- .../rustc_data_structures/src/fingerprint.rs | 15 +- .../src/fingerprint/tests.rs | 14 + compiler/rustc_data_structures/src/intern.rs | 98 + .../rustc_data_structures/src/intern/tests.rs | 59 + compiler/rustc_data_structures/src/lib.rs | 4 +- compiler/rustc_data_structures/src/ptr_key.rs | 37 - compiler/rustc_data_structures/src/sip128.rs | 61 +- .../src/snapshot_map/mod.rs | 2 + .../src/stable_hasher.rs | 69 +- .../src/stable_hasher/tests.rs | 67 +- compiler/rustc_data_structures/src/svh.rs | 4 +- .../src/thin_vec/tests.rs | 4 +- .../src/vec_map/tests.rs | 4 +- compiler/rustc_driver/src/lib.rs | 348 +- compiler/rustc_error_codes/src/error_codes.rs | 5 +- .../src/error_codes/E0038.md | 4 +- .../src/error_codes/E0183.md | 4 +- .../src/error_codes/E0192.md | 6 +- .../src/error_codes/E0521.md | 2 +- .../src/error_codes/E0581.md | 2 +- .../src/error_codes/E0604.md | 11 +- .../src/error_codes/E0660.md | 9 +- .../src/error_codes/E0661.md | 9 +- .../src/error_codes/E0662.md | 9 +- .../src/error_codes/E0663.md | 9 +- .../src/error_codes/E0664.md | 9 +- .../src/error_codes/E0668.md | 4 +- .../src/error_codes/E0669.md | 4 +- .../src/error_codes/E0772.md | 89 + .../src/error_codes/E0787.md | 28 + compiler/rustc_errors/src/diagnostic.rs | 38 +- .../rustc_errors/src/diagnostic_builder.rs | 182 +- compiler/rustc_errors/src/emitter.rs | 7 +- compiler/rustc_errors/src/json.rs | 4 +- compiler/rustc_errors/src/json/tests.rs | 2 +- compiler/rustc_errors/src/lib.rs | 28 +- compiler/rustc_errors/src/snippet.rs | 3 - compiler/rustc_expand/src/base.rs | 32 +- compiler/rustc_expand/src/config.rs | 193 +- compiler/rustc_expand/src/expand.rs | 1342 ++-- compiler/rustc_expand/src/lib.rs | 4 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 2 +- compiler/rustc_expand/src/placeholders.rs | 6 +- .../rustc_expand/src/proc_macro_server.rs | 6 +- compiler/rustc_feature/src/accepted.rs | 2 + compiler/rustc_feature/src/active.rs | 32 +- compiler/rustc_feature/src/builtin_attrs.rs | 29 +- compiler/rustc_feature/src/removed.rs | 2 + compiler/rustc_graphviz/src/tests.rs | 2 +- compiler/rustc_hir/src/arena.rs | 1 - compiler/rustc_hir/src/def.rs | 74 +- compiler/rustc_hir/src/definitions.rs | 53 +- compiler/rustc_hir/src/hir.rs | 175 +- compiler/rustc_hir/src/hir_id.rs | 7 + compiler/rustc_hir/src/intravisit.rs | 170 +- compiler/rustc_hir/src/itemlikevisit.rs | 8 +- compiler/rustc_hir/src/lang_items.rs | 33 +- compiler/rustc_hir/src/lib.rs | 1 + compiler/rustc_hir/src/stable_hash_impls.rs | 16 +- compiler/rustc_hir/src/weak_lang_items.rs | 8 +- compiler/rustc_hir_pretty/src/lib.rs | 87 +- compiler/rustc_incremental/Cargo.toml | 2 +- .../rustc_incremental/src/assert_dep_graph.rs | 10 +- compiler/rustc_incremental/src/lib.rs | 1 + .../src/persist/dirty_clean.rs | 11 +- .../src/persist/file_format.rs | 2 +- .../rustc_incremental/src/persist/fs/tests.rs | 10 +- .../rustc_incremental/src/persist/load.rs | 15 +- compiler/rustc_index/src/bit_set.rs | 12 +- compiler/rustc_index/src/lib.rs | 4 - compiler/rustc_index/src/vec.rs | 8 +- compiler/rustc_infer/src/infer/at.rs | 48 +- .../src/infer/canonical/canonicalizer.rs | 205 +- .../rustc_infer/src/infer/canonical/mod.rs | 9 +- .../src/infer/canonical/query_response.rs | 24 +- compiler/rustc_infer/src/infer/combine.rs | 70 +- compiler/rustc_infer/src/infer/equate.rs | 8 +- .../src/infer/error_reporting/mod.rs | 204 +- .../infer/error_reporting/need_type_info.rs | 90 +- .../nice_region_error/different_lifetimes.rs | 109 +- .../nice_region_error/find_anon_type.rs | 34 +- .../mismatched_static_lifetime.rs | 4 +- .../error_reporting/nice_region_error/mod.rs | 6 +- .../nice_region_error/named_anon_conflict.rs | 2 +- .../nice_region_error/placeholder_error.rs | 69 +- .../nice_region_error/static_impl_trait.rs | 48 +- .../trait_impl_difference.rs | 33 +- .../error_reporting/nice_region_error/util.rs | 92 +- .../src/infer/error_reporting/note.rs | 54 +- .../rustc_infer/src/infer/free_regions.rs | 6 +- compiler/rustc_infer/src/infer/freshen.rs | 14 +- compiler/rustc_infer/src/infer/fudge.rs | 6 +- compiler/rustc_infer/src/infer/glb.rs | 8 +- .../src/infer/higher_ranked/mod.rs | 2 +- .../src/infer/lexical_region_resolve/mod.rs | 58 +- compiler/rustc_infer/src/infer/lub.rs | 8 +- compiler/rustc_infer/src/infer/mod.rs | 98 +- .../rustc_infer/src/infer/nll_relate/mod.rs | 46 +- .../rustc_infer/src/infer/opaque_types.rs | 103 +- .../src/infer/outlives/components.rs | 2 +- .../rustc_infer/src/infer/outlives/env.rs | 11 +- .../src/infer/outlives/obligations.rs | 6 +- .../rustc_infer/src/infer/outlives/verify.rs | 2 +- compiler/rustc_infer/src/infer/projection.rs | 3 +- .../infer/region_constraints/leak_check.rs | 8 +- .../src/infer/region_constraints/mod.rs | 67 +- compiler/rustc_infer/src/infer/resolve.rs | 16 +- compiler/rustc_infer/src/infer/sub.rs | 8 +- .../rustc_infer/src/infer/type_variable.rs | 7 +- compiler/rustc_infer/src/infer/undo_log.rs | 2 + compiler/rustc_infer/src/lib.rs | 1 + compiler/rustc_infer/src/traits/mod.rs | 2 +- compiler/rustc_infer/src/traits/project.rs | 10 +- compiler/rustc_infer/src/traits/util.rs | 19 +- compiler/rustc_interface/Cargo.toml | 4 +- compiler/rustc_interface/src/callbacks.rs | 15 +- compiler/rustc_interface/src/interface.rs | 114 +- compiler/rustc_interface/src/lib.rs | 3 + compiler/rustc_interface/src/passes.rs | 91 +- compiler/rustc_interface/src/queries.rs | 13 +- compiler/rustc_interface/src/tests.rs | 17 +- compiler/rustc_interface/src/util.rs | 126 +- compiler/rustc_lint/src/array_into_iter.rs | 4 +- compiler/rustc_lint/src/builtin.rs | 65 +- compiler/rustc_lint/src/context.rs | 68 +- compiler/rustc_lint/src/early.rs | 127 +- .../src/enum_intrinsics_non_enums.rs | 2 +- compiler/rustc_lint/src/internal.rs | 89 +- compiler/rustc_lint/src/late.rs | 8 +- compiler/rustc_lint/src/levels.rs | 71 +- compiler/rustc_lint/src/lib.rs | 21 +- compiler/rustc_lint/src/methods.rs | 2 +- compiler/rustc_lint/src/non_ascii_idents.rs | 2 +- compiler/rustc_lint/src/non_fmt_panic.rs | 2 +- compiler/rustc_lint/src/nonstandard_style.rs | 2 +- compiler/rustc_lint/src/noop_method_call.rs | 4 +- compiler/rustc_lint/src/pass_by_value.rs | 95 + compiler/rustc_lint/src/traits.rs | 3 +- compiler/rustc_lint/src/types.rs | 13 +- compiler/rustc_lint_defs/src/builtin.rs | 170 +- compiler/rustc_lint_defs/src/lib.rs | 12 +- .../rustc_llvm/llvm-wrapper/LLVMWrapper.h | 2 + .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 19 - .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 28 +- compiler/rustc_macros/src/serialize.rs | 18 +- compiler/rustc_metadata/src/lib.rs | 1 + compiler/rustc_metadata/src/locator.rs | 97 +- compiler/rustc_metadata/src/native_libs.rs | 13 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 483 +- .../src/rmeta/decoder/cstore_impl.rs | 111 +- .../src/rmeta/def_path_hash_map.rs | 10 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 64 +- compiler/rustc_metadata/src/rmeta/mod.rs | 22 +- compiler/rustc_metadata/src/rmeta/table.rs | 4 - compiler/rustc_middle/Cargo.toml | 6 +- compiler/rustc_middle/src/arena.rs | 7 +- .../rustc_middle/src/dep_graph/dep_node.rs | 4 +- compiler/rustc_middle/src/dep_graph/mod.rs | 8 +- compiler/rustc_middle/src/hir/exports.rs | 28 - compiler/rustc_middle/src/hir/map/mod.rs | 254 +- compiler/rustc_middle/src/hir/mod.rs | 25 +- .../rustc_middle/src/hir/nested_filter.rs | 27 + compiler/rustc_middle/src/infer/canonical.rs | 18 +- compiler/rustc_middle/src/infer/unify_key.rs | 20 +- compiler/rustc_middle/src/lib.rs | 2 + compiler/rustc_middle/src/lint.rs | 49 +- compiler/rustc_middle/src/metadata.rs | 24 + .../src/middle/codegen_fn_attrs.rs | 2 + compiler/rustc_middle/src/middle/privacy.rs | 2 +- compiler/rustc_middle/src/middle/region.rs | 6 +- compiler/rustc_middle/src/middle/stability.rs | 6 +- .../src/mir/graph_cyclic_cache.rs | 5 +- .../rustc_middle/src/mir/interpret/mod.rs | 30 +- .../rustc_middle/src/mir/interpret/queries.rs | 10 +- compiler/rustc_middle/src/mir/mod.rs | 113 +- compiler/rustc_middle/src/mir/mono.rs | 34 +- compiler/rustc_middle/src/mir/predecessors.rs | 7 +- compiler/rustc_middle/src/mir/pretty.rs | 47 +- compiler/rustc_middle/src/mir/query.rs | 11 +- compiler/rustc_middle/src/mir/spanview.rs | 11 +- compiler/rustc_middle/src/mir/tcx.rs | 6 +- compiler/rustc_middle/src/mir/terminator.rs | 4 +- compiler/rustc_middle/src/mir/traversal.rs | 10 +- compiler/rustc_middle/src/mir/visit.rs | 52 +- compiler/rustc_middle/src/query/mod.rs | 184 +- compiler/rustc_middle/src/thir.rs | 30 +- .../rustc_middle/src/thir/abstract_const.rs | 2 +- compiler/rustc_middle/src/thir/visit.rs | 14 +- compiler/rustc_middle/src/traits/mod.rs | 36 +- compiler/rustc_middle/src/traits/query.rs | 9 +- compiler/rustc_middle/src/traits/select.rs | 4 +- .../src/traits/specialization_graph.rs | 81 +- .../src/traits/structural_impls.rs | 7 +- compiler/rustc_middle/src/ty/_match.rs | 12 +- compiler/rustc_middle/src/ty/adt.rs | 10 +- compiler/rustc_middle/src/ty/assoc.rs | 49 +- compiler/rustc_middle/src/ty/closure.rs | 40 +- compiler/rustc_middle/src/ty/codec.rs | 200 +- compiler/rustc_middle/src/ty/consts.rs | 134 +- compiler/rustc_middle/src/ty/consts/int.rs | 22 +- compiler/rustc_middle/src/ty/consts/kind.rs | 30 +- compiler/rustc_middle/src/ty/context.rs | 469 +- compiler/rustc_middle/src/ty/diagnostics.rs | 42 +- compiler/rustc_middle/src/ty/erase_regions.rs | 4 +- compiler/rustc_middle/src/ty/error.rs | 18 +- compiler/rustc_middle/src/ty/fast_reject.rs | 20 +- compiler/rustc_middle/src/ty/flags.rs | 48 +- compiler/rustc_middle/src/ty/fold.rs | 500 +- compiler/rustc_middle/src/ty/generics.rs | 7 + compiler/rustc_middle/src/ty/impls_ty.rs | 5 +- .../src/ty/inhabitedness/def_id_forest.rs | 40 +- .../rustc_middle/src/ty/inhabitedness/mod.rs | 20 +- compiler/rustc_middle/src/ty/instance.rs | 4 +- compiler/rustc_middle/src/ty/layout.rs | 67 +- compiler/rustc_middle/src/ty/mod.rs | 270 +- .../src/ty/normalize_erasing_regions.rs | 13 +- compiler/rustc_middle/src/ty/print/mod.rs | 18 +- compiler/rustc_middle/src/ty/print/pretty.rs | 329 +- compiler/rustc_middle/src/ty/query.rs | 18 +- compiler/rustc_middle/src/ty/relate.rs | 78 +- .../rustc_middle/src/ty/structural_impls.rs | 97 +- compiler/rustc_middle/src/ty/sty.rs | 359 +- compiler/rustc_middle/src/ty/subst.rs | 79 +- compiler/rustc_middle/src/ty/trait_def.rs | 33 +- compiler/rustc_middle/src/ty/util.rs | 161 +- compiler/rustc_middle/src/ty/walk.rs | 54 +- .../src/build/expr/as_constant.rs | 11 +- .../src/build/expr/as_place.rs | 15 +- .../src/build/expr/as_rvalue.rs | 1 - .../src/build/expr/category.rs | 3 +- .../rustc_mir_build/src/build/expr/into.rs | 22 +- .../rustc_mir_build/src/build/expr/stmt.rs | 32 - .../rustc_mir_build/src/build/matches/mod.rs | 103 +- .../src/build/matches/simplify.rs | 6 +- .../rustc_mir_build/src/build/matches/test.rs | 139 +- compiler/rustc_mir_build/src/build/misc.rs | 6 +- compiler/rustc_mir_build/src/build/mod.rs | 23 +- compiler/rustc_mir_build/src/build/scope.rs | 2 +- .../rustc_mir_build/src/check_unsafety.rs | 3 +- compiler/rustc_mir_build/src/lib.rs | 1 + compiler/rustc_mir_build/src/lints.rs | 29 +- compiler/rustc_mir_build/src/thir/constant.rs | 2 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 35 +- compiler/rustc_mir_build/src/thir/cx/mod.rs | 2 +- .../src/thir/pattern/check_match.rs | 57 +- .../src/thir/pattern/const_to_pat.rs | 116 +- .../src/thir/pattern/deconstruct_pat.rs | 49 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 38 +- .../rustc_mir_dataflow/src/elaborate_drops.rs | 11 +- .../rustc_mir_dataflow/src/impls/liveness.rs | 1 - .../src/impls/storage_liveness.rs | 5 - .../src/move_paths/builder.rs | 11 - compiler/rustc_mir_transform/Cargo.toml | 2 +- .../rustc_mir_transform/src/check_unsafety.rs | 11 - .../rustc_mir_transform/src/const_prop.rs | 31 +- .../rustc_mir_transform/src/coverage/debug.rs | 7 +- .../rustc_mir_transform/src/coverage/graph.rs | 12 +- .../rustc_mir_transform/src/coverage/mod.rs | 6 +- .../rustc_mir_transform/src/coverage/query.rs | 23 +- .../rustc_mir_transform/src/coverage/spans.rs | 14 +- .../rustc_mir_transform/src/coverage/tests.rs | 21 +- compiler/rustc_mir_transform/src/dest_prop.rs | 21 +- .../src/early_otherwise_branch.rs | 581 +- .../src/function_item_references.rs | 71 +- compiler/rustc_mir_transform/src/generator.rs | 17 +- compiler/rustc_mir_transform/src/inline.rs | 15 +- .../rustc_mir_transform/src/inline/cycle.rs | 2 +- compiler/rustc_mir_transform/src/lib.rs | 61 +- .../src/normalize_array_len.rs | 18 +- .../src/remove_noop_landing_pads.rs | 1 - .../src/required_consts.rs | 2 +- .../rustc_mir_transform/src/reveal_all.rs | 2 +- .../src/separate_const_switch.rs | 8 - compiler/rustc_mir_transform/src/shim.rs | 30 +- compiler/rustc_mir_transform/src/simplify.rs | 5 +- .../rustc_mir_transform/src/simplify_try.rs | 4 - .../src/uninhabited_enum_branching.rs | 17 +- .../src/unreachable_prop.rs | 13 +- compiler/rustc_monomorphize/src/collector.rs | 33 +- compiler/rustc_monomorphize/src/lib.rs | 1 + .../src/partitioning/default.rs | 4 +- .../src/partitioning/mod.rs | 32 + .../rustc_monomorphize/src/polymorphize.rs | 34 +- compiler/rustc_monomorphize/src/util.rs | 9 +- compiler/rustc_parse/src/lexer/mod.rs | 8 +- .../src/lexer/unescape_error_reporting.rs | 9 + compiler/rustc_parse/src/lib.rs | 1 + .../rustc_parse/src/parser/diagnostics.rs | 67 +- compiler/rustc_parse/src/parser/expr.rs | 57 +- compiler/rustc_parse/src/parser/generics.rs | 18 +- compiler/rustc_parse/src/parser/item.rs | 31 +- .../rustc_parse/src/parser/nonterminal.rs | 2 +- compiler/rustc_parse/src/parser/path.rs | 82 +- compiler/rustc_parse/src/parser/ty.rs | 38 + compiler/rustc_parse_format/src/lib.rs | 16 +- compiler/rustc_parse_format/src/tests.rs | 4 +- compiler/rustc_passes/src/check_attr.rs | 287 +- compiler/rustc_passes/src/check_const.rs | 23 +- compiler/rustc_passes/src/dead.rs | 114 +- compiler/rustc_passes/src/entry.rs | 33 +- compiler/rustc_passes/src/hir_id_validator.rs | 18 +- compiler/rustc_passes/src/hir_stats.rs | 12 +- compiler/rustc_passes/src/intrinsicck.rs | 30 +- compiler/rustc_passes/src/lang_items.rs | 4 +- compiler/rustc_passes/src/lib.rs | 4 + compiler/rustc_passes/src/lib_features.rs | 10 +- compiler/rustc_passes/src/liveness.rs | 56 +- compiler/rustc_passes/src/loops.rs | 9 +- compiler/rustc_passes/src/naked_functions.rs | 128 +- compiler/rustc_passes/src/reachable.rs | 26 +- compiler/rustc_passes/src/region.rs | 19 +- compiler/rustc_passes/src/stability.rs | 85 +- compiler/rustc_passes/src/upvars.rs | 14 +- compiler/rustc_passes/src/weak_lang_items.rs | 12 +- compiler/rustc_privacy/src/lib.rs | 243 +- compiler/rustc_query_impl/Cargo.toml | 2 +- compiler/rustc_query_impl/src/keys.rs | 4 +- compiler/rustc_query_impl/src/lib.rs | 5 +- .../rustc_query_impl/src/on_disk_cache.rs | 117 +- compiler/rustc_query_impl/src/plumbing.rs | 39 +- compiler/rustc_query_impl/src/stats.rs | 112 - compiler/rustc_query_impl/src/values.rs | 4 +- compiler/rustc_query_system/Cargo.toml | 2 +- .../rustc_query_system/src/dep_graph/graph.rs | 158 +- .../rustc_query_system/src/dep_graph/mod.rs | 9 +- .../src/dep_graph/serialized.rs | 19 +- compiler/rustc_query_system/src/ich/hcx.rs | 50 +- .../rustc_query_system/src/ich/impls_hir.rs | 8 +- compiler/rustc_query_system/src/ich/mod.rs | 3 +- compiler/rustc_query_system/src/lib.rs | 1 + .../rustc_query_system/src/query/config.rs | 2 +- compiler/rustc_query_system/src/query/job.rs | 155 +- compiler/rustc_query_system/src/query/mod.rs | 8 +- .../rustc_query_system/src/query/plumbing.rs | 98 +- compiler/rustc_resolve/src/access_levels.rs | 237 + .../rustc_resolve/src/build_reduced_graph.rs | 27 +- compiler/rustc_resolve/src/diagnostics.rs | 76 +- compiler/rustc_resolve/src/imports.rs | 18 +- compiler/rustc_resolve/src/late.rs | 135 +- .../rustc_resolve/src/late/diagnostics.rs | 68 +- compiler/rustc_resolve/src/late/lifetimes.rs | 390 +- compiler/rustc_resolve/src/lib.rs | 78 +- compiler/rustc_resolve/src/macros.rs | 10 +- .../rustc_save_analysis/src/dump_visitor.rs | 27 +- compiler/rustc_save_analysis/src/lib.rs | 32 +- compiler/rustc_save_analysis/src/sig.rs | 2 +- compiler/rustc_serialize/Cargo.toml | 2 +- .../rustc_serialize/src/collection_impls.rs | 85 +- compiler/rustc_serialize/src/json.rs | 207 +- compiler/rustc_serialize/src/leb128.rs | 31 +- compiler/rustc_serialize/src/opaque.rs | 97 +- compiler/rustc_serialize/src/serialize.rs | 221 +- compiler/rustc_serialize/tests/json.rs | 208 +- compiler/rustc_serialize/tests/leb128.rs | 6 +- compiler/rustc_serialize/tests/opaque.rs | 2 +- compiler/rustc_session/src/config.rs | 219 +- compiler/rustc_session/src/filesearch.rs | 34 +- compiler/rustc_session/src/lib.rs | 2 + compiler/rustc_session/src/options.rs | 83 +- compiler/rustc_session/src/parse.rs | 4 + compiler/rustc_session/src/search_paths.rs | 26 +- compiler/rustc_session/src/session.rs | 4 - compiler/rustc_span/Cargo.toml | 6 +- compiler/rustc_span/src/def_id.rs | 33 +- compiler/rustc_span/src/hygiene.rs | 57 +- compiler/rustc_span/src/lev_distance.rs | 68 +- compiler/rustc_span/src/lev_distance/tests.rs | 22 +- compiler/rustc_span/src/lib.rs | 123 +- compiler/rustc_span/src/source_map.rs | 40 +- compiler/rustc_span/src/span_encoding.rs | 9 + compiler/rustc_span/src/symbol.rs | 75 +- compiler/rustc_symbol_mangling/src/legacy.rs | 48 +- compiler/rustc_symbol_mangling/src/lib.rs | 4 +- compiler/rustc_symbol_mangling/src/v0.rs | 41 +- compiler/rustc_target/src/abi/call/mod.rs | 37 +- compiler/rustc_target/src/abi/call/s390x.rs | 22 +- compiler/rustc_target/src/abi/call/x86.rs | 22 +- compiler/rustc_target/src/abi/mod.rs | 18 + compiler/rustc_target/src/asm/aarch64.rs | 9 +- compiler/rustc_target/src/asm/arm.rs | 45 +- compiler/rustc_target/src/asm/avr.rs | 3 +- compiler/rustc_target/src/asm/bpf.rs | 11 +- compiler/rustc_target/src/asm/hexagon.rs | 3 +- compiler/rustc_target/src/asm/mips.rs | 3 +- compiler/rustc_target/src/asm/mod.rs | 171 +- compiler/rustc_target/src/asm/msp430.rs | 81 + compiler/rustc_target/src/asm/nvptx.rs | 3 +- compiler/rustc_target/src/asm/powerpc.rs | 3 +- compiler/rustc_target/src/asm/riscv.rs | 11 +- compiler/rustc_target/src/asm/s390x.rs | 3 +- compiler/rustc_target/src/asm/spirv.rs | 3 +- compiler/rustc_target/src/asm/wasm.rs | 3 +- compiler/rustc_target/src/asm/x86.rs | 26 +- .../src/spec/aarch64_linux_android.rs | 4 +- .../src/spec/aarch64_unknown_hermit.rs | 1 + .../src/spec/aarch64_unknown_linux_gnu.rs | 1 + .../spec/aarch64_unknown_none_hermitkernel.rs | 16 + compiler/rustc_target/src/spec/abi.rs | 86 +- .../rustc_target/src/spec/android_base.rs | 8 +- .../src/spec/armv6k_nintendo_3ds.rs | 1 + .../spec/armv7_unknown_linux_uclibceabi.rs | 23 + .../rustc_target/src/spec/avr_gnu_base.rs | 6 +- .../src/spec/i686_pc_windows_msvc.rs | 2 +- .../src/spec/i686_uwp_windows_msvc.rs | 2 +- compiler/rustc_target/src/spec/l4re_base.rs | 17 +- .../src/spec/mips64_openwrt_linux_musl.rs | 26 + compiler/rustc_target/src/spec/mod.rs | 46 +- .../src/spec/wasm32_unknown_emscripten.rs | 3 +- .../src/spec/wasm32_unknown_unknown.rs | 2 +- compiler/rustc_target/src/spec/wasm32_wasi.rs | 2 +- .../src/spec/x86_64_unknown_l4re_uclibc.rs | 5 +- .../src/spec/x86_64_unknown_linux_gnu.rs | 1 + compiler/rustc_trait_selection/src/lib.rs | 1 + .../rustc_trait_selection/src/opaque_types.rs | 8 +- .../src/traits/auto_trait.rs | 37 +- .../src/traits/chalk_fulfill.rs | 9 +- .../src/traits/codegen.rs | 14 +- .../src/traits/coherence.rs | 291 +- .../src/traits/const_evaluatable.rs | 36 +- .../src/traits/error_reporting/mod.rs | 358 +- .../error_reporting/on_unimplemented.rs | 7 +- .../src/traits/error_reporting/suggestions.rs | 329 +- .../src/traits/fulfill.rs | 23 +- .../rustc_trait_selection/src/traits/mod.rs | 4 +- .../src/traits/object_safety.rs | 31 +- .../src/traits/on_unimplemented.rs | 83 +- .../src/traits/project.rs | 364 +- .../src/traits/query/dropck_outlives.rs | 2 +- .../src/traits/query/normalize.rs | 28 +- .../src/traits/query/type_op/mod.rs | 61 +- .../src/traits/relationships.rs | 2 +- .../src/traits/select/candidate_assembly.rs | 210 +- .../src/traits/select/confirmation.rs | 148 +- .../src/traits/select/mod.rs | 173 +- .../src/traits/specialize/mod.rs | 21 +- .../traits/specialize/specialization_graph.rs | 37 +- .../src/traits/structural_match.rs | 4 - .../rustc_trait_selection/src/traits/wf.rs | 81 +- compiler/rustc_traits/Cargo.toml | 6 +- compiler/rustc_traits/src/chalk/db.rs | 54 +- compiler/rustc_traits/src/chalk/lowering.rs | 199 +- compiler/rustc_traits/src/chalk/mod.rs | 30 +- compiler/rustc_traits/src/dropck_outlives.rs | 14 +- .../src/normalize_erasing_regions.rs | 4 +- .../src/normalize_projection_ty.rs | 5 +- compiler/rustc_ty_utils/src/assoc.rs | 136 + compiler/rustc_ty_utils/src/instance.rs | 51 +- compiler/rustc_ty_utils/src/lib.rs | 2 + .../rustc_ty_utils/src/representability.rs | 13 +- compiler/rustc_ty_utils/src/ty.rs | 146 +- compiler/rustc_type_ir/src/lib.rs | 117 +- compiler/rustc_typeck/Cargo.toml | 2 + compiler/rustc_typeck/src/astconv/errors.rs | 159 +- compiler/rustc_typeck/src/astconv/generics.rs | 2 +- compiler/rustc_typeck/src/astconv/mod.rs | 218 +- compiler/rustc_typeck/src/bounds.rs | 46 +- compiler/rustc_typeck/src/check/_match.rs | 4 +- compiler/rustc_typeck/src/check/callee.rs | 38 +- compiler/rustc_typeck/src/check/cast.rs | 26 +- compiler/rustc_typeck/src/check/check.rs | 334 +- compiler/rustc_typeck/src/check/closure.rs | 10 +- compiler/rustc_typeck/src/check/coercion.rs | 20 +- .../rustc_typeck/src/check/compare_method.rs | 68 +- compiler/rustc_typeck/src/check/demand.rs | 234 +- compiler/rustc_typeck/src/check/dropck.rs | 18 +- compiler/rustc_typeck/src/check/expr.rs | 199 +- compiler/rustc_typeck/src/check/fallback.rs | 4 +- .../rustc_typeck/src/check/fn_ctxt/_impl.rs | 37 +- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 353 +- .../rustc_typeck/src/check/fn_ctxt/mod.rs | 7 +- .../src/check/fn_ctxt/suggestions.rs | 139 +- .../rustc_typeck/src/check/gather_locals.rs | 12 +- .../src/check/generator_interior.rs | 71 +- .../check/generator_interior/drop_ranges.rs | 279 + .../drop_ranges/cfg_build.rs | 558 ++ .../drop_ranges/cfg_propagate.rs | 92 + .../drop_ranges/cfg_visualize.rs | 77 + .../drop_ranges/record_consumed_borrow.rs | 118 + compiler/rustc_typeck/src/check/intrinsic.rs | 7 +- .../rustc_typeck/src/check/method/confirm.rs | 29 +- compiler/rustc_typeck/src/check/method/mod.rs | 17 +- .../rustc_typeck/src/check/method/probe.rs | 65 +- .../rustc_typeck/src/check/method/suggest.rs | 170 +- compiler/rustc_typeck/src/check/mod.rs | 65 +- compiler/rustc_typeck/src/check/op.rs | 118 +- compiler/rustc_typeck/src/check/pat.rs | 60 +- compiler/rustc_typeck/src/check/place_op.rs | 15 +- compiler/rustc_typeck/src/check/regionck.rs | 16 +- compiler/rustc_typeck/src/check/upvar.rs | 544 +- compiler/rustc_typeck/src/check/wfcheck.rs | 600 +- compiler/rustc_typeck/src/check/writeback.rs | 22 +- .../rustc_typeck/src/coherence/builtin.rs | 8 +- .../src/coherence/inherent_impls_overlap.rs | 26 +- compiler/rustc_typeck/src/coherence/mod.rs | 22 - compiler/rustc_typeck/src/coherence/orphan.rs | 208 +- compiler/rustc_typeck/src/collect.rs | 316 +- .../rustc_typeck/src/collect/item_bounds.rs | 6 +- compiler/rustc_typeck/src/collect/type_of.rs | 480 +- .../src/constrained_generic_params.rs | 27 +- compiler/rustc_typeck/src/expr_use_visitor.rs | 16 +- compiler/rustc_typeck/src/hir_wf_check.rs | 8 +- compiler/rustc_typeck/src/impl_wf_check.rs | 4 +- .../src/impl_wf_check/min_specialization.rs | 14 +- compiler/rustc_typeck/src/lib.rs | 9 +- .../rustc_typeck/src/mem_categorization.rs | 5 +- .../src/outlives/implicit_infer.rs | 15 +- compiler/rustc_typeck/src/outlives/mod.rs | 7 +- compiler/rustc_typeck/src/outlives/utils.rs | 17 +- .../rustc_typeck/src/variance/constraints.rs | 20 +- compiler/rustc_typeck/src/variance/terms.rs | 2 +- config.toml.example | 7 +- git-commit-hash | 2 +- library/alloc/Cargo.toml | 2 +- library/alloc/src/alloc.rs | 6 +- library/alloc/src/boxed.rs | 9 +- library/alloc/src/collections/binary_heap.rs | 49 +- library/alloc/src/collections/btree/map.rs | 23 +- .../alloc/src/collections/btree/map/tests.rs | 4 +- library/alloc/src/collections/btree/set.rs | 7 +- library/alloc/src/collections/linked_list.rs | 2 + library/alloc/src/collections/mod.rs | 4 +- .../alloc/src/collections/vec_deque/mod.rs | 260 +- library/alloc/src/fmt.rs | 2 +- library/alloc/src/lib.rs | 27 +- library/alloc/src/macros.rs | 1 + library/alloc/src/raw_vec.rs | 2 +- library/alloc/src/rc.rs | 129 +- library/alloc/src/slice.rs | 7 +- library/alloc/src/string.rs | 17 +- library/alloc/src/sync.rs | 134 +- library/alloc/src/vec/into_iter.rs | 2 +- library/alloc/src/vec/mod.rs | 45 +- .../alloc/src/vec/spec_from_iter_nested.rs | 10 +- library/alloc/tests/boxed.rs | 2 +- library/alloc/tests/lib.rs | 3 +- library/alloc/tests/linked_list.rs | 27 +- library/alloc/tests/slice.rs | 5 +- library/alloc/tests/str.rs | 134 + library/alloc/tests/string.rs | 2 +- library/alloc/tests/vec.rs | 16 +- library/alloc/tests/vec_deque.rs | 12 +- library/core/Cargo.toml | 4 +- library/core/benches/str.rs | 29 +- library/core/benches/str/char_count.rs | 107 + library/core/benches/str/corpora.rs | 88 + library/core/src/array/mod.rs | 3 +- .../stream.rs => async_iter/async_iter.rs} | 56 +- .../src/{stream => async_iter}/from_iter.rs | 18 +- .../core/src/{stream => async_iter}/mod.rs | 69 +- library/core/src/cell.rs | 16 +- library/core/src/char/decode.rs | 31 +- library/core/src/char/methods.rs | 17 +- library/core/src/char/mod.rs | 17 +- library/core/src/cmp.rs | 112 +- library/core/src/convert/mod.rs | 36 +- library/core/src/fmt/mod.rs | 22 + library/core/src/future/into_future.rs | 2 +- library/core/src/future/join.rs | 2 +- library/core/src/future/pending.rs | 5 +- library/core/src/hash/mod.rs | 2 +- library/core/src/intrinsics.rs | 27 +- library/core/src/iter/adapters/map.rs | 6 +- library/core/src/iter/adapters/mod.rs | 79 +- library/core/src/iter/mod.rs | 2 +- library/core/src/iter/sources.rs | 1 + library/core/src/iter/sources/empty.rs | 12 +- library/core/src/iter/traits/accum.rs | 8 +- library/core/src/iter/traits/collect.rs | 36 +- library/core/src/iter/traits/iterator.rs | 132 +- library/core/src/iter/traits/mod.rs | 16 +- library/core/src/lazy.rs | 1 + library/core/src/lib.rs | 55 +- library/core/src/macros/mod.rs | 55 +- library/core/src/mem/maybe_uninit.rs | 139 +- library/core/src/num/bignum.rs | 63 +- library/core/src/num/f32.rs | 48 +- library/core/src/num/f64.rs | 48 +- library/core/src/num/int_log10.rs | 247 +- library/core/src/num/int_macros.rs | 21 +- library/core/src/num/mod.rs | 28 +- library/core/src/num/nonzero.rs | 53 +- library/core/src/num/saturating.rs | 73 + library/core/src/num/uint_macros.rs | 30 +- library/core/src/num/wrapping.rs | 80 + library/core/src/ops/arith.rs | 35 +- library/core/src/ops/bit.rs | 11 + library/core/src/ops/control_flow.rs | 2 - library/core/src/ops/try_trait.rs | 9 + library/core/src/option.rs | 61 +- library/core/src/panic/panic_info.rs | 20 +- library/core/src/panic/unwind_safe.rs | 6 +- library/core/src/panicking.rs | 30 +- library/core/src/pin.rs | 249 +- library/core/src/prelude/v1.rs | 5 +- library/core/src/primitive_docs.rs | 59 +- library/core/src/ptr/const_ptr.rs | 12 +- library/core/src/ptr/mut_ptr.rs | 16 +- library/core/src/ptr/non_null.rs | 9 +- library/core/src/ptr/unique.rs | 3 + library/core/src/result.rs | 85 +- library/core/src/slice/ascii.rs | 17 +- library/core/src/slice/cmp.rs | 23 +- library/core/src/slice/iter.rs | 42 +- library/core/src/slice/mod.rs | 46 +- library/core/src/slice/sort.rs | 6 +- library/core/src/str/count.rs | 136 + library/core/src/str/iter.rs | 5 +- library/core/src/str/mod.rs | 89 +- library/core/src/str/validations.rs | 13 - library/core/src/sync/atomic.rs | 14 +- library/core/src/task/poll.rs | 2 +- library/core/src/task/wake.rs | 24 + library/core/src/time.rs | 301 +- library/core/tests/char.rs | 27 + library/core/tests/cmp.rs | 1 - library/core/tests/future.rs | 8 + library/core/tests/hash/mod.rs | 8 + library/core/tests/intrinsics.rs | 23 +- .../core/tests/iter/adapters/intersperse.rs | 4 +- library/core/tests/iter/adapters/peekable.rs | 6 +- library/core/tests/iter/traits/iterator.rs | 64 +- library/core/tests/lib.rs | 15 +- library/core/tests/num/bignum.rs | 35 + library/core/tests/ops.rs | 6 + library/core/tests/pin_macro.rs | 33 + library/core/tests/ptr.rs | 16 +- library/core/tests/slice.rs | 102 + library/core/tests/waker.rs | 22 + library/panic_abort/Cargo.toml | 2 +- library/panic_unwind/Cargo.toml | 2 +- library/panic_unwind/src/gcc.rs | 3 + library/panic_unwind/src/lib.rs | 4 + library/portable-simd/Cargo.toml | 1 + .../portable-simd/crates/core_simd/Cargo.toml | 3 + .../crates/core_simd/examples/nbody.rs | 10 +- .../crates/core_simd/src/intrinsics.rs | 34 +- .../crates/core_simd/src/masks.rs | 42 +- .../portable-simd/crates/core_simd/src/ops.rs | 382 +- .../crates/core_simd/src/round.rs | 41 - .../crates/core_simd/src/vector.rs | 30 + .../crates/core_simd/src/vector/float.rs | 39 +- .../crates/core_simd/tests/cast.rs | 37 + .../crates/core_simd/tests/ops_macros.rs | 2 + .../crates/core_simd/tests/round.rs | 2 + .../portable-simd/crates/std_float/Cargo.toml | 13 + .../portable-simd/crates/std_float/src/lib.rs | 165 + library/proc_macro/Cargo.toml | 2 +- library/proc_macro/src/bridge/client.rs | 5 +- library/proc_macro/src/lib.rs | 1 + library/profiler_builtins/Cargo.toml | 2 +- library/rustc-std-workspace-alloc/Cargo.toml | 2 +- library/rustc-std-workspace-core/Cargo.toml | 2 +- library/rustc-std-workspace-std/Cargo.toml | 2 +- library/std/Cargo.toml | 8 +- library/std/src/collections/hash/map.rs | 14 + library/std/src/collections/hash/map/tests.rs | 41 +- library/std/src/collections/hash/set.rs | 17 +- library/std/src/collections/mod.rs | 2 +- library/std/src/env.rs | 10 + library/std/src/error.rs | 661 +- library/std/src/error/tests.rs | 405 + library/std/src/ffi/c_str.rs | 136 +- library/std/src/ffi/c_str/tests.rs | 8 - library/std/src/ffi/os_str.rs | 18 +- library/std/src/fs.rs | 21 +- library/std/src/fs/tests.rs | 16 + library/std/src/io/buffered/bufreader.rs | 4 +- library/std/src/io/buffered/bufwriter.rs | 6 +- library/std/src/io/buffered/mod.rs | 8 +- library/std/src/io/cursor.rs | 10 +- library/std/src/io/error.rs | 214 +- library/std/src/io/error/repr_bitpacked.rs | 401 + library/std/src/io/error/repr_unpacked.rs | 50 + library/std/src/io/error/tests.rs | 82 +- library/std/src/io/impls.rs | 9 +- library/std/src/io/mod.rs | 49 +- library/std/src/io/stdio.rs | 25 +- library/std/src/io/tests.rs | 4 +- library/std/src/keyword_docs.rs | 2 +- library/std/src/lib.rs | 110 +- library/std/src/macros.rs | 5 + library/std/src/net/mod.rs | 6 +- library/std/src/net/tcp.rs | 2 +- library/std/src/net/udp.rs | 6 +- library/std/src/os/fd/owned.rs | 49 + library/std/src/os/linux/raw.rs | 1 + library/std/src/os/raw/mod.rs | 150 +- library/std/src/os/unix/ffi/os_str.rs | 2 + library/std/src/os/unix/fs.rs | 8 +- library/std/src/os/unix/io/raw.rs | 1 + library/std/src/os/unix/net/addr.rs | 72 +- library/std/src/os/wasi/fs.rs | 23 +- library/std/src/os/wasi/net/mod.rs | 20 + library/std/src/os/windows/io/handle.rs | 48 + library/std/src/os/windows/io/socket.rs | 91 + library/std/src/panic.rs | 117 + library/std/src/panicking.rs | 124 +- library/std/src/path.rs | 123 +- library/std/src/path/tests.rs | 58 + library/std/src/prelude/v1.rs | 6 +- library/std/src/primitive_docs.rs | 59 +- library/std/src/process.rs | 47 +- library/std/src/rt.rs | 2 +- library/std/src/sync/mutex.rs | 7 +- library/std/src/sync/rwlock.rs | 14 +- library/std/src/sys/common/alloc.rs | 10 +- library/std/src/sys/hermit/fs.rs | 10 +- library/std/src/sys/hermit/mod.rs | 4 +- library/std/src/sys/hermit/net.rs | 49 +- library/std/src/sys/hermit/stdio.rs | 8 +- library/std/src/sys/hermit/thread.rs | 2 +- library/std/src/sys/hermit/time.rs | 8 - library/std/src/sys/itron/condvar.rs | 13 +- library/std/src/sys/itron/thread.rs | 10 +- library/std/src/sys/itron/time.rs | 9 - library/std/src/sys/sgx/mod.rs | 6 +- library/std/src/sys/sgx/net.rs | 12 +- library/std/src/sys/sgx/path.rs | 8 +- library/std/src/sys/sgx/time.rs | 8 - library/std/src/sys/solid/abi/sockets.rs | 3 + library/std/src/sys/solid/fs.rs | 27 +- library/std/src/sys/solid/mod.rs | 4 +- library/std/src/sys/solid/net.rs | 12 +- library/std/src/sys/solid/os.rs | 6 +- library/std/src/sys/solid/path.rs | 8 +- library/std/src/sys/solid/time.rs | 2 +- library/std/src/sys/unix/fd.rs | 17 +- library/std/src/sys/unix/fs.rs | 128 +- library/std/src/sys/unix/l4re.rs | 4 +- library/std/src/sys/unix/mod.rs | 7 +- library/std/src/sys/unix/net.rs | 14 +- library/std/src/sys/unix/os.rs | 22 +- library/std/src/sys/unix/path.rs | 44 +- .../src/sys/unix/process/process_common.rs | 6 + .../src/sys/unix/process/process_fuchsia.rs | 16 +- .../std/src/sys/unix/process/process_unix.rs | 13 +- .../sys/unix/process/process_unix/tests.rs | 7 +- .../src/sys/unix/process/process_vxworks.rs | 8 +- library/std/src/sys/unix/thread.rs | 10 +- library/std/src/sys/unix/time.rs | 19 - library/std/src/sys/unsupported/common.rs | 4 +- library/std/src/sys/unsupported/os.rs | 4 +- library/std/src/sys/unsupported/process.rs | 9 + library/std/src/sys/unsupported/time.rs | 8 - library/std/src/sys/wasi/fd.rs | 4 + library/std/src/sys/wasi/fs.rs | 4 +- library/std/src/sys/wasi/mod.rs | 41 +- library/std/src/sys/wasi/net.rs | 66 +- library/std/src/sys/wasi/stdio.rs | 2 +- library/std/src/sys/wasi/thread.rs | 8 +- library/std/src/sys/wasi/time.rs | 10 +- library/std/src/sys/windows/c.rs | 14 +- library/std/src/sys/windows/fs.rs | 21 +- library/std/src/sys/windows/handle.rs | 21 +- library/std/src/sys/windows/mod.rs | 8 +- library/std/src/sys/windows/net.rs | 74 +- library/std/src/sys/windows/path.rs | 16 + library/std/src/sys/windows/process.rs | 33 +- library/std/src/sys/windows/process/tests.rs | 11 + library/std/src/sys/windows/stdio.rs | 16 +- library/std/src/sys/windows/thread.rs | 4 +- library/std/src/sys/windows/time.rs | 8 - library/std/src/sys_common/backtrace.rs | 46 - library/std/src/sys_common/fs.rs | 4 +- library/std/src/sys_common/io.rs | 8 +- library/std/src/sys_common/net.rs | 6 +- library/std/src/thread/local.rs | 1 + library/std/src/thread/mod.rs | 109 +- library/std/src/thread/scoped.rs | 316 + library/std/src/time.rs | 120 +- library/std/src/time/monotonic.rs | 116 - library/std/src/time/tests.rs | 31 +- library/std/tests/run-time-detect.rs | 12 +- library/stdarch/crates/core_arch/avx512bw.md | 36 +- library/stdarch/crates/core_arch/avx512f.md | 72 +- .../stdarch/crates/core_arch/avx512vbmi2.md | 153 - .../crates/core_arch/src/core_arch_docs.md | 24 +- .../crates/core_arch/src/riscv64/mod.rs | 8 +- .../crates/core_arch/src/riscv_shared/mod.rs | 310 +- .../crates/core_arch/src/x86/avx512f.rs | 984 +++ .../crates/core_arch/src/x86/avx512vbmi2.rs | 575 +- .../stdarch/crates/core_arch/src/x86/sse42.rs | 6 +- .../crates/core_arch/src/x86_64/sse42.rs | 2 +- .../std_detect/src/detect/arch/aarch64.rs | 90 +- .../crates/std_detect/src/detect/arch/arm.rs | 1 + .../crates/std_detect/src/detect/arch/mips.rs | 1 + .../std_detect/src/detect/arch/mips64.rs | 1 + .../crates/std_detect/src/detect/arch/mod.rs | 56 + .../std_detect/src/detect/arch/powerpc.rs | 1 + .../std_detect/src/detect/arch/powerpc64.rs | 1 + .../std_detect/src/detect/arch/riscv.rs | 1 + .../crates/std_detect/src/detect/arch/x86.rs | 1 + .../std_detect/src/detect/error_macros.rs | 171 - .../crates/std_detect/src/detect/macros.rs | 50 + .../crates/std_detect/src/detect/mod.rs | 57 +- .../std_detect/src/detect/os/aarch64.rs | 4 + .../std_detect/src/detect/os/linux/aarch64.rs | 4 +- library/stdarch/crates/std_detect/src/lib.rs | 11 +- .../crates/std_detect/tests/cpu-detection.rs | 3 +- library/test/Cargo.toml | 2 +- library/test/src/cli.rs | 14 +- library/test/src/console.rs | 6 +- library/test/src/formatters/json.rs | 13 +- library/test/src/formatters/junit.rs | 5 +- library/test/src/formatters/pretty.rs | 27 +- library/test/src/formatters/terse.rs | 25 +- library/test/src/lib.rs | 3 +- library/test/src/test_result.rs | 13 +- library/test/src/tests.rs | 61 +- library/test/src/time.rs | 5 +- library/test/src/types.rs | 6 +- library/unwind/Cargo.toml | 2 +- library/unwind/src/libunwind.rs | 3 + src/bootstrap/Cargo.toml | 14 +- src/bootstrap/bin/main.rs | 14 + src/bootstrap/bootstrap.py | 34 +- src/bootstrap/bootstrap_test.py | 4 +- src/bootstrap/build.rs | 2 + src/bootstrap/builder.rs | 200 +- src/bootstrap/builder/tests.rs | 4 +- src/bootstrap/cc_detect.rs | 7 +- src/bootstrap/compile.rs | 21 +- src/bootstrap/config.rs | 9 +- src/bootstrap/dist.rs | 46 +- src/bootstrap/doc.rs | 8 +- src/bootstrap/lib.rs | 40 +- src/bootstrap/native.rs | 117 +- src/bootstrap/setup.rs | 53 +- src/bootstrap/test.rs | 18 +- src/bootstrap/util.rs | 5 +- src/build_helper/lib.rs | 24 +- src/ci/cpu-usage-over-time.py | 44 +- .../host-x86_64/dist-arm-linux/Dockerfile | 13 - .../host-x86_64/dist-armhf-linux/Dockerfile | 13 - .../host-x86_64/dist-armv7-linux/Dockerfile | 13 - .../host-x86_64/dist-x86_64-musl/Dockerfile | 4 +- .../docker/host-x86_64/mingw-check/Dockerfile | 10 +- .../host-x86_64/x86_64-gnu-tools/Dockerfile | 2 +- src/ci/docker/scripts/cmake.sh | 6 +- src/ci/docker/scripts/musl-toolchain.sh | 4 +- src/ci/github-actions/ci.yml | 30 +- src/ci/pgo.sh | 16 +- src/doc/book/.github/workflows/main.yml | 4 +- .../listing-02-04/output.txt | 29 +- .../output.txt | 2 + .../listing-09-10/output.txt | 19 +- .../no-listing-03-closures/Cargo.toml | 6 - .../no-listing-03-closures/src/main.rs | 14 - .../listing-14-07/add/Cargo.lock | 4 +- .../listing-14-07/add/Cargo.toml | 2 +- .../add/add_one}/Cargo.toml | 2 +- .../add/{add-one => add_one}/src/lib.rs | 0 .../listing-14-07/add/adder/Cargo.toml | 2 +- .../add/Cargo.lock | 4 +- .../add/Cargo.toml | 2 +- .../add/add-one/Cargo.toml | 6 - .../add/add_one}/Cargo.toml | 2 +- .../add/{add-one => add_one}/src/lib.rs | 0 .../add/adder/Cargo.toml | 3 +- .../add/Cargo.lock | 4 +- .../add/Cargo.toml | 2 +- .../add/{add-one => add_one}/Cargo.toml | 2 +- .../add/{add-one => add_one}/src/lib.rs | 0 .../add/adder/Cargo.toml | 2 +- .../add/Cargo.lock | 4 +- .../add/Cargo.toml | 2 +- .../add/add_one}/Cargo.toml | 2 +- .../add/{add-one => add_one}/src/lib.rs | 0 .../add/adder/Cargo.toml | 2 +- .../output-only-02-add-one/add/Cargo.lock | 4 +- .../output-only-02-add-one/add/Cargo.toml | 2 +- .../add/add-one/src/lib.rs | 7 - .../add/adder/Cargo.toml | 2 +- .../output-only-03-use-rand/add/Cargo.lock | 4 +- .../output-only-03-use-rand/add/Cargo.toml | 2 +- .../add/{add-one => add_one}/Cargo.toml | 2 +- .../add/{add-one => add_one}/src/lib.rs | 0 .../add/adder/Cargo.toml | 2 +- .../Cargo.lock | 6 - .../output.txt | 13 - .../src/lib.rs | 3 - .../listing-18-26/src/main.rs | 4 +- .../listing-19-20/output.txt | 5 - .../output.txt | 9 +- src/doc/book/nostarch/chapter02.md | 5 + src/doc/book/nostarch/chapter03.md | 5 + src/doc/book/nostarch/chapter04.md | 5 + src/doc/book/nostarch/chapter05.md | 5 + src/doc/book/nostarch/chapter06.md | 5 + src/doc/book/nostarch/chapter08.md | 375 +- src/doc/book/nostarch/chapter09.md | 400 +- src/doc/book/nostarch/chapter10.md | 5 + src/doc/book/nostarch/chapter11.md | 5 + src/doc/book/nostarch/chapter12.md | 5 + src/doc/book/nostarch/chapter14.md | 1006 +++ src/doc/book/nostarch/chapter15.md | 2001 +++++ src/doc/book/nostarch/chapter16.md | 1204 +++ src/doc/book/nostarch/chapter17.md | 1208 +++ src/doc/book/nostarch/chapter18.md | 1242 +++ src/doc/book/rust-toolchain | 2 +- .../src/ch02-00-guessing-game-tutorial.md | 4 +- src/doc/book/src/ch06-02-match.md | 2 +- .../book/src/ch08-00-common-collections.md | 4 +- src/doc/book/src/ch08-01-vectors.md | 140 +- src/doc/book/src/ch08-02-strings.md | 137 +- src/doc/book/src/ch08-03-hash-maps.md | 26 +- src/doc/book/src/ch09-00-error-handling.md | 20 +- ...ch09-01-unrecoverable-errors-with-panic.md | 56 +- .../ch09-02-recoverable-errors-with-result.md | 252 +- .../src/ch09-03-to-panic-or-not-to-panic.md | 38 +- ...improving-error-handling-and-modularity.md | 2 +- .../src/ch14-02-publishing-to-crates-io.md | 7 +- src/doc/book/src/ch14-03-cargo-workspaces.md | 78 +- src/doc/book/src/ch15-02-deref.md | 30 +- src/doc/book/src/ch15-04-rc.md | 5 +- src/doc/book/src/ch15-06-reference-cycles.md | 12 +- src/doc/book/src/ch16-01-threads.md | 54 +- src/doc/book/src/ch16-03-shared-state.md | 10 +- src/doc/book/src/ch17-02-trait-objects.md | 59 - .../book/src/ch17-03-oo-design-patterns.md | 40 +- src/doc/book/src/ch18-03-pattern-syntax.md | 30 +- src/doc/book/src/ch20-02-multithreaded.md | 4 +- src/doc/book/src/title-page.md | 5 +- src/doc/book/tools/doc-to-md.sh | 2 +- src/doc/book/tools/megadiff.sh | 2 +- src/doc/book/tools/nostarch.sh | 2 +- src/doc/book/tools/src/bin/concat_chapters.rs | 11 +- src/doc/embedded-book/src/intro/index.md | 8 + src/doc/man/rustc.1 | 2 +- src/doc/man/rustdoc.1 | 2 +- src/doc/nomicon/src/exception-safety.md | 2 +- src/doc/nomicon/src/ffi.md | 4 +- src/doc/nomicon/src/hrtb.md | 36 +- src/doc/nomicon/src/leaking.md | 2 +- src/doc/nomicon/src/other-reprs.md | 8 +- src/doc/nomicon/src/send-and-sync.md | 2 +- src/doc/nomicon/src/vec/vec-alloc.md | 12 +- src/doc/reference/src/SUMMARY.md | 1 + src/doc/reference/src/attributes/codegen.md | 7 +- src/doc/reference/src/expressions.md | 28 +- .../src/expressions/method-call-expr.md | 9 + .../src/expressions/operator-expr.md | 69 +- .../reference/src/expressions/struct-expr.md | 8 +- .../src/expressions/underscore-expr.md | 17 + src/doc/reference/src/glossary.md | 2 +- src/doc/reference/src/identifiers.md | 2 + .../reference/src/items/associated-items.md | 33 + src/doc/reference/src/items/constant-items.md | 4 +- src/doc/reference/src/items/functions.md | 8 +- src/doc/reference/src/items/generics.md | 5 +- src/doc/reference/src/macros-by-example.md | 30 +- src/doc/reference/src/names/preludes.md | 34 +- src/doc/reference/src/paths.md | 16 +- src/doc/reference/src/patterns.md | 10 +- src/doc/reference/src/tokens.md | 42 +- src/doc/reference/src/trait-bounds.md | 2 +- src/doc/rustc-dev-guide/book.toml | 3 + src/doc/rustc-dev-guide/src/SUMMARY.md | 11 +- .../rustc-dev-guide/src/about-this-guide.md | 4 +- src/doc/rustc-dev-guide/src/asm.md | 367 + .../rustc-dev-guide/src/backend/codegen.md | 2 +- .../rustc-dev-guide/src/backend/monomorph.md | 4 +- .../src/building/bootstrapping.md | 2 +- .../src/building/compiler-documenting.md | 2 +- .../src/building/how-to-build-and-run.md | 29 +- .../rustc-dev-guide/src/building/suggested.md | 2 +- src/doc/rustc-dev-guide/src/compiler-src.md | 4 +- src/doc/rustc-dev-guide/src/compiletest.md | 223 - src/doc/rustc-dev-guide/src/contributing.md | 4 +- src/doc/rustc-dev-guide/src/conventions.md | 21 +- src/doc/rustc-dev-guide/src/crates-io.md | 6 +- .../src/diagnostics/diagnostic-items.md | 4 +- .../src/diagnostics/lintstore.md | 10 +- .../rustc-dev-guide/src/getting-started.md | 174 +- src/doc/rustc-dev-guide/src/git.md | 13 +- .../src/implementing_new_features.md | 43 +- .../rustc-dev-guide/src/macro-expansion.md | 2 +- src/doc/rustc-dev-guide/src/overview.md | 4 +- src/doc/rustc-dev-guide/src/profiling.md | 7 +- src/doc/rustc-dev-guide/src/rustdoc.md | 9 +- .../src/stabilization_guide.md | 3 +- .../src/test-implementation.md | 2 +- src/doc/rustc-dev-guide/src/tests/adding.md | 663 +- src/doc/rustc-dev-guide/src/tests/ci.md | 73 + .../rustc-dev-guide/src/tests/compiletest.md | 526 ++ src/doc/rustc-dev-guide/src/tests/crater.md | 45 + src/doc/rustc-dev-guide/src/tests/docker.md | 52 + src/doc/rustc-dev-guide/src/tests/headers.md | 403 + src/doc/rustc-dev-guide/src/tests/intro.md | 464 +- src/doc/rustc-dev-guide/src/tests/perf.md | 50 + src/doc/rustc-dev-guide/src/tests/running.md | 145 +- src/doc/rustc-dev-guide/src/tests/ui.md | 500 ++ src/doc/rustc-dev-guide/src/ty.md | 2 +- src/doc/rustc-dev-guide/src/walkthrough.md | 20 +- src/doc/rustc/src/SUMMARY.md | 6 + src/doc/rustc/src/codegen-options/index.md | 13 +- src/doc/rustc/src/command-line-arguments.md | 6 + .../src}/instrument-coverage.md | 96 +- src/doc/rustc/src/linker-plugin-lto.md | 42 + src/doc/rustc/src/platform-support.md | 19 +- .../rustc/src/platform-support/TEMPLATE.md | 52 + .../aarch64-unknown-none-hermitkernel.md | 77 + .../armv7-unknown-linux-uclibceabi.md | 121 + .../armv7-unknown-linux-uclibceabihf.md | 2 +- .../mips64-openwrt-linux-musl.md | 28 + src/doc/rustc/src/platform-support/openbsd.md | 56 + src/doc/rustc/src/target-tier-policy.md | 43 +- src/doc/rustc/src/tests/index.md | 2 +- src/doc/rustdoc/src/command-line-arguments.md | 15 +- src/doc/rustdoc/src/documentation-tests.md | 3 +- src/doc/rustdoc/src/references.md | 4 +- src/doc/rustdoc/src/unstable-features.md | 36 + .../src/compiler-flags/branch-protection.md | 18 + .../src/compiler-flags/cf-protection.md | 40 + .../src/compiler-flags/report-time.md | 7 +- .../src/compiler-flags/sanitizer.md | 20 +- .../source-based-code-coverage.md | 5 - .../src/language-features/asm-const.md | 4 +- .../asm-experimental-arch.md | 21 +- .../src/language-features/asm-sym.md | 4 +- .../src/language-features/asm-unwind.md | 4 +- .../src/language-features/c-unwind.md | 17 +- .../src/language-features/cfg-panic.md | 38 - .../src/library-features/llvm-asm.md | 190 - src/etc/check_missing_items.py | 4 +- src/etc/htmldocck.py | 14 +- src/etc/natvis/libstd.natvis | 2 +- src/etc/{pre-commit.sh => pre-push.sh} | 2 +- src/librustdoc/Cargo.toml | 9 +- src/librustdoc/askama.toml | 2 + src/librustdoc/clean/auto_trait.rs | 10 +- src/librustdoc/clean/blanket_impl.rs | 211 +- src/librustdoc/clean/cfg.rs | 53 +- src/librustdoc/clean/inline.rs | 16 +- src/librustdoc/clean/mod.rs | 102 +- src/librustdoc/clean/render_macro_matchers.rs | 240 + src/librustdoc/clean/simplify.rs | 14 +- src/librustdoc/clean/types.rs | 152 +- src/librustdoc/clean/utils.rs | 44 +- src/librustdoc/config.rs | 8 +- src/librustdoc/core.rs | 120 +- src/librustdoc/doctest.rs | 16 +- src/librustdoc/formats/cache.rs | 26 +- src/librustdoc/html/format.rs | 134 +- src/librustdoc/html/highlight.rs | 3 +- src/librustdoc/html/layout.rs | 20 +- src/librustdoc/html/markdown.rs | 22 +- src/librustdoc/html/markdown/tests.rs | 48 +- src/librustdoc/html/render/context.rs | 48 +- src/librustdoc/html/render/mod.rs | 741 +- src/librustdoc/html/render/print_item.rs | 215 +- src/librustdoc/html/render/search_index.rs | 70 +- src/librustdoc/html/render/span_map.rs | 25 +- src/librustdoc/html/render/templates.rs | 20 - src/librustdoc/html/render/write_shared.rs | 13 +- src/librustdoc/html/sources.rs | 1 - src/librustdoc/html/static/css/rustdoc.css | 693 +- src/librustdoc/html/static/css/settings.css | 28 +- src/librustdoc/html/static/css/themes/ayu.css | 91 +- .../html/static/css/themes/dark.css | 82 +- .../html/static/css/themes/light.css | 84 +- .../html/static/images/favicon-16x16.png | Bin 2214 -> 715 bytes .../html/static/images/favicon-32x32.png | Bin 2919 -> 1125 bytes .../html/static/images/rust-logo.png | Bin 5758 -> 0 bytes .../html/static/images/rust-logo.svg | 61 + src/librustdoc/html/static/js/main.js | 159 +- src/librustdoc/html/static/js/search.js | 270 +- src/librustdoc/html/static/js/settings.js | 39 +- .../html/static/js/source-script.js | 10 +- src/librustdoc/html/static/js/storage.js | 54 +- src/librustdoc/html/static_files.rs | 5 +- src/librustdoc/html/templates/page.html | 123 +- src/librustdoc/html/templates/print_item.html | 52 +- src/librustdoc/html/tests.rs | 48 +- src/librustdoc/html/url_parts_builder.rs | 67 +- .../html/url_parts_builder/tests.rs | 10 + src/librustdoc/json/conversions.rs | 11 +- src/librustdoc/json/mod.rs | 23 +- src/librustdoc/lib.rs | 78 +- .../passes/collect_intra_doc_links.rs | 723 +- .../passes/collect_intra_doc_links/early.rs | 250 +- src/librustdoc/passes/collect_trait_impls.rs | 89 +- src/librustdoc/passes/html_tags.rs | 74 +- src/librustdoc/passes/strip_hidden.rs | 4 +- src/librustdoc/passes/strip_private.rs | 2 +- src/librustdoc/passes/stripper.rs | 15 +- src/librustdoc/scrape_examples.rs | 42 +- src/librustdoc/visit_ast.rs | 19 +- src/librustdoc/visit_lib.rs | 2 +- src/rustdoc-json-types/lib.rs | 17 +- src/stage0.json | 564 +- src/test/assembly/aarch64-pointer-auth.rs | 22 + src/test/assembly/asm/msp430-types.rs | 158 + src/test/codegen/async-fn-debug-msvc.rs | 2 +- src/test/codegen/async-fn-debug.rs | 2 +- src/test/codegen/branch-protection.rs | 46 + src/test/codegen/cf-protection.rs | 38 + src/test/codegen/debug-vtable.rs | 88 +- .../debuginfo-generic-closure-env-names.rs | 89 + src/test/codegen/fastcall-inreg.rs | 55 +- src/test/codegen/function-arguments.rs | 63 +- src/test/codegen/generator-debug-msvc.rs | 2 +- src/test/codegen/generator-debug.rs | 2 +- src/test/codegen/naked-functions.rs | 40 +- src/test/codegen/naked-noinline.rs | 1 - src/test/codegen/no-output-asm-is-volatile.rs | 14 - src/test/codegen/packed.rs | 4 +- .../riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs | 26 +- .../codegen/sanitizer_memtag_attr_check.rs | 12 + src/test/codegen/scalar-pair-bool.rs | 10 +- src/test/codegen/thread-local.rs | 4 +- src/test/codegen/transmute-scalar.rs | 4 +- src/test/codegen/union-abi.rs | 2 +- .../codegen/unwind-abis/aapcs-unwind-abi.rs | 31 + .../codegen/unwind-abis/cdecl-unwind-abi.rs | 29 + .../unwind-abis/fastcall-unwind-abi.rs | 31 + .../codegen/unwind-abis/sysv64-unwind-abi.rs | 31 + .../unwind-abis/vectorcall-unwind-abi.rs | 31 + .../codegen/unwind-abis/win64-unwind-abi.rs | 31 + src/test/codegen/unwind-and-panic-abort.rs | 2 +- src/test/codegen/used_with_arg.rs | 10 + src/test/debuginfo/captured-fields-1.rs | 29 +- src/test/debuginfo/function-names.rs | 25 +- src/test/debuginfo/generator-objects.rs | 20 +- src/test/debuginfo/issue-57822.rs | 12 +- src/test/debuginfo/type-names.rs | 25 +- src/test/debuginfo/unsized.rs | 73 +- .../var-captured-in-nested-closure.rs | 2 +- src/test/debuginfo/vec-slices.rs | 48 +- src/test/incremental/cache_file_headers.rs | 2 +- src/test/incremental/hashes/extern_mods.rs | 12 +- src/test/incremental/hashes/inherent_impls.rs | 84 +- src/test/incremental/hashes/inline_asm.rs | 130 +- src/test/incremental/hashes/trait_defs.rs | 252 +- src/test/incremental/hashes/trait_impls.rs | 58 +- src/test/incremental/hashes/type_defs.rs | 14 +- .../issue-92987-provisional-dep-node.rs | 24 + ..._allocation.main.ConstProp.after.32bit.mir | 5 +- ..._allocation.main.ConstProp.after.64bit.mir | 5 +- ...allocation2.main.ConstProp.after.32bit.mir | 5 +- ...allocation2.main.ConstProp.after.64bit.mir | 5 +- ...allocation3.main.ConstProp.after.32bit.mir | 3 - ...allocation3.main.ConstProp.after.64bit.mir | 3 - ...d[0].SimplifyCfg-elaborate-drops.after.mir | 3 - ...motion_extern_static.BAR.PromoteTemps.diff | 6 +- ...d[0].SimplifyCfg-elaborate-drops.after.mir | 3 - ...motion_extern_static.FOO.PromoteTemps.diff | 6 +- ...e_oob_for_slices.main.ConstProp.32bit.diff | 2 +- ...e_oob_for_slices.main.ConstProp.64bit.diff | 2 +- ..._prop_fails_gracefully.main.ConstProp.diff | 2 +- .../invalid_constant.main.ConstProp.diff | 69 + .../mir-opt/const_prop/invalid_constant.rs | 23 + ...table_variable_no_prop.main.ConstProp.diff | 3 - .../read_immutable_static.main.ConstProp.diff | 6 - .../const_prop/ref_deref.main.ConstProp.diff | 2 +- .../ref_deref.main.PromoteTemps.diff | 2 +- .../ref_deref_project.main.ConstProp.diff | 2 +- .../ref_deref_project.main.PromoteTemps.diff | 2 +- .../slice_len.main.ConstProp.32bit.diff | 2 +- .../slice_len.main.ConstProp.64bit.diff | 2 +- src/test/mir-opt/coverage_graphviz.rs | 4 +- ...wise_branch.opt1.EarlyOtherwiseBranch.diff | 10 +- ...wise_branch.opt2.EarlyOtherwiseBranch.diff | 10 +- ...wise_branch.opt3.EarlyOtherwiseBranch.diff | 77 + src/test/mir-opt/early_otherwise_branch.rs | 10 + ...ement_tuple.opt1.EarlyOtherwiseBranch.diff | 14 +- ...re-SimplifyConstCondition-final.after.diff | 10 +- ...ch_68867.try_sum.EarlyOtherwiseBranch.diff | 10 +- ...nch_noopt.noopt2.EarlyOtherwiseBranch.diff | 60 - .../mir-opt/early_otherwise_branch_noopt.rs | 10 - ...ess.no_deref_ptr.EarlyOtherwiseBranch.diff | 43 + ...ness.no_downcast.EarlyOtherwiseBranch.diff | 30 + .../early_otherwise_branch_soundness.rs | 32 + .../inline/inline_retag.bar.Inline.after.mir | 4 +- src/test/mir-opt/instrument_coverage.rs | 4 +- .../issue_73223.main.PreCodegen.32bit.diff | 2 +- .../issue_73223.main.PreCodegen.64bit.diff | 2 +- ..._73223.main.SimplifyArmIdentity.32bit.diff | 2 +- ..._73223.main.SimplifyArmIdentity.64bit.diff | 2 +- ...trinsics.discriminant.LowerIntrinsics.diff | 6 +- ...s.full_tested_match.PromoteTemps.after.mir | 2 +- .../nll/named_lifetimes_basic.use_x.nll.0.mir | 4 +- ...asts.SimplifyCfg-elaborate-drops.after.mir | 2 +- ...main.SimplifyCfg-elaborate-drops.after.mir | 4 +- ...hable_asm.main.UnreachablePropagation.diff | 75 - src/test/mir-opt/unreachable_asm.rs | 24 - ...ble_asm_2.main.UnreachablePropagation.diff | 85 - src/test/mir-opt/unreachable_asm_2.rs | 26 - src/test/pretty/ast-stmt-expr-attr.rs | 80 +- src/test/pretty/block-comment-wchar.pp | 8 +- src/test/pretty/delimited-token-groups.rs | 20 +- src/test/pretty/dollar-crate.pp | 7 +- src/test/pretty/issue-4264.pp | 78 +- .../issue-68710-field-attr-proc-mac-lost.rs | 11 +- src/test/pretty/llvm-asm-clobbers.rs | 3 - src/test/pretty/llvm-asm-options.rs | 11 - src/test/pretty/macro_rules.rs | 4 +- src/test/pretty/match-naked-expr-medium.rs | 8 +- src/test/pretty/raw-str-nonexpr.rs | 6 +- src/test/pretty/stmt_expr_attributes.rs | 78 +- src/test/pretty/use-tree.rs | 23 + src/test/pretty/vec-comments.pp | 32 +- .../atomic-lock-free/atomic_lock_free.rs | 2 +- .../codegen-options-parsing/Makefile | 4 +- .../core-no-fp-fmt-parse/Makefile | 2 +- .../coverage-llvmir/Makefile | 2 +- .../coverage-llvmir/filecheck.testprog.txt | 6 +- .../coverage-reports/Makefile | 8 +- .../expected_show_coverage.closure.txt | 6 +- .../expected_show_coverage.issue-85461.txt | 2 +- .../expected_show_coverage.issue-93054.txt | 29 + .../expected_show_coverage.no_cov_crate.txt | 66 +- .../expected_show_coverage.unused_mod.txt | 4 + .../expected_show_coverage.uses_crate.txt | 10 +- ...pected_show_coverage.uses_inline_crate.txt | 8 +- .../run-make-fulldeps/coverage/issue-93054.rs | 28 + .../lib/inline_always_with_dead_code.rs | 2 +- .../coverage/lib/unused_mod_helper.rs | 3 + .../coverage/lib/used_crate.rs | 2 +- .../coverage/no_cov_crate.rs | 52 +- .../run-make-fulldeps/coverage/unused_mod.rs | 6 + .../crate-hash-rustc-version/Makefile | 2 +- .../interdependent-c-libraries/Makefile | 2 +- .../intrinsic-unreachable/exit-ret.rs | 5 +- .../intrinsic-unreachable/exit-unreachable.rs | 5 +- src/test/run-make-fulldeps/issue-19371/foo.rs | 2 +- .../run-make-fulldeps/libs-and-bins/Makefile | 6 - .../run-make-fulldeps/libs-and-bins/foo.rs | 4 - .../libtest-json/output-default.json | 2 +- .../libtest-json/output-stdout-success.json | 2 +- src/test/run-make-fulldeps/link-arg/Makefile | 2 +- .../metadata-flag-frobs-symbols/Makefile | 2 +- .../no-builtins-lto/Makefile | 2 +- .../output-with-hyphens/Makefile | 3 +- .../output-with-hyphens/foo-bar.rs | 3 - .../pointer-auth-link-with-c/Makefile | 14 + .../pointer-auth-link-with-c/test.c | 1 + .../pointer-auth-link-with-c/test.rs | 8 + .../run-make-fulldeps/redundant-libs/Makefile | 2 +- .../static-nobundle/Makefile | 4 +- src/test/run-make/const_fn_mir/dump.mir | 32 +- src/test/run-make/dep-graph/Makefile | 4 +- .../incremental-session-fail/Makefile | 1 - .../raw-dylib-alt-calling-convention/Makefile | 7 +- .../raw-dylib-alt-calling-convention/lib.rs | 7 +- .../output.txt | 2 - src/test/run-make/raw-dylib-c/Makefile | 7 +- .../run-make/raw-dylib-link-ordinal/Makefile | 6 +- .../raw-dylib-stdcall-ordinal/Makefile | 23 + .../raw-dylib-stdcall-ordinal/driver.rs | 5 + .../expected_output.txt | 2 + .../exporter-gnu.def | 4 + .../exporter-msvc.def | 4 + .../raw-dylib-stdcall-ordinal/exporter.c | 11 + .../run-make/raw-dylib-stdcall-ordinal/lib.rs | 20 + .../scrape.mk | 3 +- .../rustdoc-scrape-examples-test/Makefile | 6 + .../examples/ex.rs | 6 + .../rustdoc-scrape-examples-test/src/lib.rs | 3 + .../Makefile | 5 + .../examples/ex.rs | 8 + .../src/lib.rs | 3 + src/test/run-make/static-pie/Makefile | 25 +- .../static-pie/check_clang_version.sh | 20 + .../run-make/static-pie/check_gcc_version.sh | 20 + src/test/rustdoc-gui/anchors.goml | 50 +- .../rustdoc-gui/check_info_sign_position.goml | 2 + .../rustdoc-gui/code-blocks-overflow.goml | 2 +- src/test/rustdoc-gui/code-sidebar-toggle.goml | 1 + .../rustdoc-gui/docblock-big-code-mobile.goml | 2 +- .../rustdoc-gui/docblock-table-overflow.goml | 4 +- src/test/rustdoc-gui/escape-key.goml | 4 +- src/test/rustdoc-gui/font-weight.goml | 2 +- src/test/rustdoc-gui/hash-item-expansion.goml | 8 +- src/test/rustdoc-gui/headers-color.goml | 9 +- src/test/rustdoc-gui/headings.goml | 90 +- .../huge-collection-of-constants.goml | 4 +- src/test/rustdoc-gui/implementors.goml | 4 + src/test/rustdoc-gui/item-info-width.goml | 5 +- src/test/rustdoc-gui/list_code_block.goml | 1 + src/test/rustdoc-gui/mobile.goml | 27 + src/test/rustdoc-gui/search-filter.goml | 35 +- .../rustdoc-gui/search-result-colors.goml | 1 + .../rustdoc-gui/search-result-display.goml | 3 +- .../rustdoc-gui/search-result-keyword.goml | 1 + ...rch-tab-selection-if-current-is-empty.goml | 2 + src/test/rustdoc-gui/sidebar-mobile.goml | 36 +- src/test/rustdoc-gui/sidebar-source-code.goml | 4 + src/test/rustdoc-gui/sidebar.goml | 61 +- src/test/rustdoc-gui/source-code-page.goml | 1 + src/test/rustdoc-gui/src-font-size.goml | 5 +- src/test/rustdoc-gui/src/lib2/lib.rs | 45 +- .../rustdoc-gui/src/staged_api}/Cargo.lock | 5 +- .../rustdoc-gui/src/staged_api/Cargo.toml | 11 + src/test/rustdoc-gui/src/staged_api/lib.rs | 10 + src/test/rustdoc-gui/src/test_docs/Cargo.toml | 4 + src/test/rustdoc-gui/src/test_docs/lib.rs | 11 + src/test/rustdoc-gui/theme-change.goml | 17 + src/test/rustdoc-gui/theme-in-history.goml | 25 + src/test/rustdoc-gui/toggle-docs-mobile.goml | 14 +- src/test/rustdoc-gui/toggle-docs.goml | 4 + .../rustdoc-gui/trait-sidebar-item-order.goml | 13 +- .../rustdoc-gui/type-declation-overflow.goml | 16 +- src/test/rustdoc-js-std/multi-query.js | 10 - src/test/rustdoc-js-std/typed-query.js | 2 + src/test/rustdoc-js/generics-multi-trait.js | 32 + src/test/rustdoc-js/generics-multi-trait.rs | 12 + src/test/rustdoc-json/enums/variant_struct.rs | 4 +- .../enums/variant_tuple_struct.rs | 2 + .../rustdoc-json/impls/blanket_with_local.rs | 18 + src/test/rustdoc-json/type/dyn.rs | 21 + src/test/rustdoc-json/type/fn_lifetime.rs | 28 + src/test/rustdoc-json/type/generic_default.rs | 33 + src/test/rustdoc-ui/block-doc-comment.rs | 16 + src/test/rustdoc-ui/block-doc-comment.stdout | 5 + src/test/rustdoc-ui/check-attr-test.rs | 7 - src/test/rustdoc-ui/check-attr-test.stderr | 60 +- src/test/rustdoc-ui/check-attr.rs | 10 - src/test/rustdoc-ui/check-attr.stderr | 50 +- .../intra-doc/disambiguator-mismatch.rs | 5 + .../intra-doc/disambiguator-mismatch.stderr | 13 +- src/test/rustdoc-ui/intra-doc/macro-rules.rs | 9 + .../html-as-generics-no-suggestions.rs | 38 + .../html-as-generics-no-suggestions.stderr | 38 + .../suggestions/html-as-generics.fixed | 32 + .../suggestions/html-as-generics.rs | 32 + .../suggestions/html-as-generics.stderr | 73 + src/test/rustdoc/associated-consts.rs | 4 +- src/test/rustdoc/async-fn.rs | 6 +- src/test/rustdoc/auto_aliases.rs | 2 +- src/test/rustdoc/blanket-reexport-item.rs | 2 +- src/test/rustdoc/cap-lints.rs | 2 +- src/test/rustdoc/const-display.rs | 23 +- src/test/rustdoc/const-generics/add-impl.rs | 2 +- .../const-generics/const-generic-slice.rs | 4 +- .../const-generics/const-generics-docs.rs | 4 +- src/test/rustdoc/const-generics/const-impl.rs | 12 +- src/test/rustdoc/crate-version-escape.rs | 3 +- src/test/rustdoc/crate-version.rs | 2 +- src/test/rustdoc/decl_macro.rs | 12 +- src/test/rustdoc/deref-const-fn.rs | 2 +- src/test/rustdoc/deref-mut-methods.rs | 2 +- src/test/rustdoc/deref-recursive-pathbuf.rs | 4 +- src/test/rustdoc/deref-recursive.rs | 4 +- src/test/rustdoc/deref-typedef.rs | 8 +- src/test/rustdoc/doc-auto-cfg.rs | 10 +- src/test/rustdoc/doc-cfg-hide.rs | 2 +- src/test/rustdoc/double-quote-escape.rs | 2 +- src/test/rustdoc/empty-impls.rs | 6 +- src/test/rustdoc/ensure-src-link.rs | 2 +- src/test/rustdoc/extern-default-method.rs | 4 +- src/test/rustdoc/external-macro-src.rs | 4 +- src/test/rustdoc/generic-impl.rs | 6 +- .../auxiliary/extern-inherent-impl-dep.rs | 11 + .../auxiliary/extern-lang-item-impl-dep.rs | 29 + src/test/rustdoc/intra-doc/crate-relative.rs | 13 + .../rustdoc/intra-doc/extern-inherent-impl.rs | 8 + .../intra-doc/extern-lang-item-impl.rs | 11 + src/test/rustdoc/intra-doc/mod-relative.rs | 17 + .../prim-self.rs} | 11 +- .../self-cache.rs} | 0 src/test/rustdoc/issue-16265-1.rs | 6 +- src/test/rustdoc/issue-16265-2.rs | 4 +- src/test/rustdoc/issue-26606.rs | 2 +- src/test/rustdoc/issue-29503.rs | 10 +- src/test/rustdoc/issue-33302.rs | 9 +- src/test/rustdoc/issue-53812.rs | 13 +- src/test/rustdoc/issue-86620.rs | 4 +- src/test/rustdoc/issue-88600.rs | 5 +- src/test/rustdoc/lifetime-name.rs | 5 + ...o-generated-macro.macro_linebreak_pre.html | 6 + ...o-generated-macro.macro_morestuff_pre.html | 15 + src/test/rustdoc/macro-generated-macro.rs | 39 + src/test/rustdoc/macros.rs | 8 +- src/test/rustdoc/method-list.rs | 4 +- ...ing-doc-comments-and-attrs.S1_top-doc.html | 4 +- ...ing-doc-comments-and-attrs.S3_top-doc.html | 3 + .../rustdoc/mixing-doc-comments-and-attrs.rs | 7 + src/test/rustdoc/negative-impl-sidebar.rs | 2 +- .../primitive/primitive-generic-impl.rs | 3 +- src/test/rustdoc/recursive-deref-sidebar.rs | 4 +- src/test/rustdoc/reexports-priv.rs | 8 +- src/test/rustdoc/reexports.rs | 4 +- src/test/rustdoc/sidebar-items.rs | 24 +- src/test/rustdoc/sidebar-link-generation.rs | 2 +- .../rustdoc/sidebar-links-to-foreign-impl.rs | 8 +- src/test/rustdoc/sized_trait.rs | 6 +- src/test/rustdoc/source-version-separator.rs | 30 + src/test/rustdoc/spotlight-from-dependency.rs | 2 +- src/test/rustdoc/src-links-auto-impls.rs | 12 +- ...rip-block-doc-comments-stars.docblock.html | 2 + .../rustdoc/strip-block-doc-comments-stars.rs | 11 + src/test/rustdoc/structfields.rs | 8 +- src/test/rustdoc/thread-local-src.rs | 4 +- src/test/rustdoc/titles.rs | 7 +- src/test/rustdoc/toggle-item-contents.rs | 2 +- .../trait-impl-items-links-and-anchors.rs | 56 +- src/test/rustdoc/trait-src-link.rs | 12 +- src/test/rustdoc/trait_alias.rs | 2 +- src/test/rustdoc/tuple-struct-fields-doc.rs | 14 + src/test/rustdoc/typedef.rs | 2 +- src/test/rustdoc/union.rs | 2 +- .../version-separator-without-source.rs | 23 + src/test/rustdoc/where-clause-order.rs | 16 +- .../auxiliary/issue-40001-plugin.rs | 2 +- .../deriving-encodable-decodable-box.rs | 2 +- ...riving-encodable-decodable-cell-refcell.rs | 2 +- .../internal-lints/pass_ty_by_ref.stderr | 80 - .../internal-lints/pass_ty_by_ref_self.stderr | 20 - .../qualified_ty_ty_ctxt.stderr | 4 +- .../internal-lints/query_stability.rs | 24 + .../internal-lints/query_stability.stderr | 39 + .../query_stability_incorrect.rs | 15 + .../query_stability_incorrect.stderr | 17 + ...ss_ty_by_ref.rs => rustc_pass_by_value.rs} | 56 +- .../internal-lints/rustc_pass_by_value.stderr | 128 + ...ef_self.rs => rustc_pass_by_value_self.rs} | 28 +- .../rustc_pass_by_value_self.stderr | 38 + src/test/ui-fulldeps/issue-14021.rs | 2 +- src/test/ui-fulldeps/issue-4016.rs | 2 +- src/test/ui-fulldeps/issue-4036.rs | 2 +- src/test/ui/abi/abi-sysv64-register-usage.rs | 62 +- src/test/ui/aligned_enum_cast.rs | 15 + src/test/ui/allocator/not-an-allocator.stderr | 16 +- src/test/ui/asm/aarch64/parse-error.stderr | 14 +- .../bad-template.aarch64_mirunsafeck.stderr | 8 +- .../bad-template.aarch64_thirunsafeck.stderr | 8 +- .../bad-template.x86_64_mirunsafeck.stderr | 8 +- .../bad-template.x86_64_thirunsafeck.stderr | 8 +- src/test/ui/asm/naked-functions.rs | 83 +- src/test/ui/asm/naked-functions.stderr | 218 +- src/test/ui/asm/reg-conflict.rs | 20 + src/test/ui/asm/reg-conflict.stderr | 10 + src/test/ui/asm/x86_64/parse-error.stderr | 14 +- .../assoc-const-eq-missing.rs | 26 + .../assoc-const-eq-missing.stderr | 21 + .../assoc-const-ty-mismatch.rs | 31 + .../assoc-const-ty-mismatch.stderr | 26 + src/test/ui/associated-consts/assoc-const.rs | 22 + src/test/ui/associated-consts/issue-93835.rs | 10 + .../ui/associated-consts/issue-93835.stderr | 66 + .../ui/associated-consts/shadowed-const.rs | 23 + .../associated-consts/shadowed-const.stderr | 8 + .../bad-bounds-on-assoc-in-trait.rs | 1 - .../bad-bounds-on-assoc-in-trait.stderr | 6 +- .../ui/associated-type-bounds/trait-params.rs | 1 - .../ui/associated-type-bounds/union-bounds.rs | 1 - .../associated-types-path-2.stderr | 10 + .../associated-types-stream.rs | 2 - .../hr-associated-type-bound-1.stderr | 2 + .../hr-associated-type-bound-object.stderr | 2 - .../hr-associated-type-bound-param-1.stderr | 2 + .../hr-associated-type-bound-param-2.rs | 1 - .../hr-associated-type-bound-param-2.stderr | 18 +- .../hr-associated-type-bound-param-3.stderr | 2 + .../hr-associated-type-bound-param-4.stderr | 2 + .../hr-associated-type-bound-param-5.rs | 1 - .../hr-associated-type-bound-param-5.stderr | 12 +- src/test/ui/associated-types/issue-50301.rs | 1 - src/test/ui/associated-types/issue-91069.rs | 24 + src/test/ui/ast-json/ast-json-ice.rs | 9 - src/test/ui/async-await/async-fn-nonsend.rs | 29 +- .../ui/async-await/async-fn-nonsend.stderr | 51 +- ...edition-error-in-non-macro-position.stderr | 28 +- .../await-keyword/2018-edition-error.stderr | 20 +- .../interior-with-const-generic-expr.rs | 26 + src/test/ui/async-await/issue-76547.stderr | 14 +- src/test/ui/async-await/issue-93197.rs | 16 + src/test/ui/async-await/issue-93648.rs | 12 + .../async-await/issues/issue-63388-1.stderr | 6 +- .../ret-impl-trait-one.stderr | 5 +- .../partial-drop-partial-reinit.rs | 29 + .../partial-drop-partial-reinit.stderr | 27 + .../proper-span-for-type-error.fixed | 11 + .../async-await/proper-span-for-type-error.rs | 11 + .../proper-span-for-type-error.stderr | 16 + .../ui/async-await/unresolved_type_param.rs | 12 +- .../async-await/unresolved_type_param.stderr | 26 +- .../ui/attributes/key-value-expansion.stderr | 14 +- src/test/ui/attributes/used_with_arg.rs | 19 + src/test/ui/attributes/used_with_arg.stderr | 18 + .../ui/attributes/used_with_multi_args.rs | 6 + .../ui/attributes/used_with_multi_args.stderr | 8 + .../ui/auto-traits/suspicious-impls-lint.rs | 50 + .../auto-traits/suspicious-impls-lint.stderr | 82 + ...ypeck-default-trait-impl-precedence.stderr | 2 + .../ui/autoref-autoderef/deref-into-array.rs | 17 + src/test/ui/binop/issue-28837.stderr | 16 +- src/test/ui/borrowck/borrowck-asm.rs | 83 - src/test/ui/borrowck/borrowck-asm.stderr | 81 - src/test/ui/borrowck/issue-64453.rs | 2 +- src/test/ui/borrowck/issue-64453.stderr | 3 +- src/test/ui/borrowck/issue-93093.rs | 14 + src/test/ui/borrowck/issue-93093.stderr | 12 + src/test/ui/box/issue-78459-ice.rs | 6 + src/test/ui/c-variadic/variadic-ffi-4.stderr | 2 +- src/test/ui/cfg/cfg-panic-abort.rs | 2 +- src/test/ui/cfg/cfg-panic.rs | 2 +- ...-compat-crate-attributes-using-cfg_attr.rs | 4 + ...pat-crate-attributes-using-cfg_attr.stderr | 22 +- src/test/ui/chalkify/assert.rs | 6 + .../ui/chalkify/chalk_initial_program.stderr | 3 + src/test/ui/chalkify/impl_wf.stderr | 2 + src/test/ui/chalkify/impl_wf_2.stderr | 2 + src/test/ui/chalkify/println.rs | 3 +- src/test/ui/chalkify/trait-objects.rs | 3 +- src/test/ui/chalkify/type_wf.stderr | 1 - src/test/ui/check-cfg/empty-names.rs | 10 + src/test/ui/check-cfg/empty-names.stderr | 10 + src/test/ui/check-cfg/empty-values.rs | 6 + src/test/ui/check-cfg/empty-values.stderr | 2 + .../invalid-arguments.anything_else.stderr | 2 + ...nvalid-arguments.names_simple_ident.stderr | 2 + src/test/ui/check-cfg/invalid-arguments.rs | 10 + ...valid-arguments.values_simple_ident.stderr | 2 + ...id-arguments.values_string_literals.stderr | 2 + src/test/ui/check-cfg/invalid-cfg-name.rs | 14 + src/test/ui/check-cfg/invalid-cfg-name.stderr | 10 + src/test/ui/check-cfg/invalid-cfg-value.rs | 17 + .../ui/check-cfg/invalid-cfg-value.stderr | 10 + .../ui/check-static-values-constraints.rs | 2 +- .../ui/check-static-values-constraints.stderr | 8 +- .../arrays-completely-captured.rs | 1 + .../arrays-completely-captured.stderr | 9 +- .../destructure_patterns.rs | 2 + .../destructure_patterns.stderr | 42 +- .../diagnostics/union.rs | 25 + .../diagnostics/union.stderr | 18 + .../2229_closure_analysis/nested-closure.rs | 1 + .../nested-closure.stderr | 9 +- .../2229_closure_analysis/repr_packed.rs | 1 + .../2229_closure_analysis/repr_packed.stderr | 21 +- .../ui/closures/issue-84044-drop-non-mut.rs | 6 + .../closures/issue-84044-drop-non-mut.stderr | 11 + ...re-print-generic-trim-off-verbose-2.stderr | 2 +- .../closure-print-generic-verbose-2.stderr | 2 +- src/test/ui/codemap_tests/unicode.stderr | 2 +- src/test/ui/coherence/auxiliary/error_lib.rs | 1 + .../ui/coherence/auxiliary/option_future.rs | 8 + .../coherence-negative-outlives-lifetimes.rs | 12 + ...herence-negative-outlives-lifetimes.stderr | 11 + .../coherence-overlap-negate-alias-strict.rs | 4 +- ...herence-overlap-negate-alias-strict.stderr | 1 - .../coherence-overlap-negate-strict.rs | 4 +- ...herence-overlap-negate-use-feature-gate.rs | 2 +- .../coherence-overlap-negative-trait.rs | 2 +- .../coherence-overlap-negative-trait2.rs | 17 + .../coherence-overlap-trait-alias.rs | 4 +- .../coherence-overlap-trait-alias.stderr | 3 +- .../coherence-overlap-with-regions.rs | 16 + .../cfg-arg-invalid-1.rs | 2 +- .../cfg-arg-invalid-1.stderr | 2 +- .../cfg-arg-invalid-9.rs | 4 + .../cfg-arg-invalid-9.stderr | 2 + .../const-generic-default-wont-borrowck.rs | 6 + ...const-generic-default-wont-borrowck.stderr | 9 + .../const-generics/defaults/doesnt_infer.rs | 2 +- .../defaults/doesnt_infer.stderr | 2 +- .../defaults/pretty-printing-ast.stdout | 2 +- .../defaults/rp_impl_trait_fail.stderr | 2 + .../deref-into-array-generic.rs | 27 + .../generic_arg_infer/array-in-sig.rs | 12 - .../generic_arg_infer/array-in-sig.stderr | 9 - .../generic_arg_infer/in-signature.rs | 61 + .../generic_arg_infer/in-signature.stderr | 119 + .../infer_arg_and_const_arg.rs | 12 + .../generic_arg_infer/issue-91614.rs | 2 +- .../generic_arg_infer/issue-91614.stderr | 2 +- src/test/ui/const-generics/issue-93647.rs | 2 +- src/test/ui/const-generics/issue-93647.stderr | 5 +- .../issues/issue-62878.full.stderr | 11 +- .../ui/const-generics/issues/issue-62878.rs | 3 +- .../ui/const-generics/issues/issue-90318.rs | 4 +- .../const-generics/issues/issue-90318.stderr | 20 +- .../ui/const-generics/issues/issue-92186.rs | 12 + ...st-missing-braces-without-turbofish.stderr | 24 +- .../forbid-self-no-normalize.rs | 15 + .../forbid-self-no-normalize.stderr | 14 + .../ui/const-generics/nested-type.full.stderr | 4 +- .../ui/const-generics/nested-type.min.stderr | 4 +- src/test/ui/const-generics/nested-type.rs | 2 +- .../issue-89013-no-kw.rs | 3 +- .../issue-89013-no-kw.stderr | 31 +- .../parser-error-recovery/issue-89013.rs | 3 +- .../parser-error-recovery/issue-89013.stderr | 31 +- .../types-mismatch-const-args.full.stderr | 17 +- .../types-mismatch-const-args.min.stderr | 13 +- .../types-mismatch-const-args.rs | 2 + src/test/ui/consts/const-block-const-bound.rs | 23 + .../ui/consts/const-block-const-bound.stderr | 43 + src/test/ui/consts/const-call.rs | 2 +- src/test/ui/consts/const-call.stderr | 4 +- .../const-eval/const-eval-overflow-4b.stderr | 6 + .../const-eval/heap/alloc_intrinsic_errors.rs | 3 +- .../heap/alloc_intrinsic_errors.stderr | 2 +- .../heap/alloc_intrinsic_zero_sized.rs | 16 + .../const-eval/heap/dealloc_intrinsic.rs | 36 + .../heap/dealloc_intrinsic_dangling.rs | 22 + .../heap/dealloc_intrinsic_dangling.stderr | 15 + .../heap/dealloc_intrinsic_duplicate.rs | 13 + .../heap/dealloc_intrinsic_duplicate.stderr | 9 + .../dealloc_intrinsic_incorrect_layout.rs | 29 + .../dealloc_intrinsic_incorrect_layout.stderr | 27 + .../heap/dealloc_intrinsic_zero_sized.rs | 17 + .../const-eval/ub-nonnull.chalk.64bit.stderr | 9 + .../const-eval/ub-wide-ptr.chalk.64bit.stderr | 9 + .../const-extern-fn-call-extern-fn.rs | 4 +- .../const-extern-fn-call-extern-fn.stderr | 8 +- src/test/ui/consts/const-fn-error.rs | 5 +- src/test/ui/consts/const-fn-error.stderr | 29 +- .../consts/const-fn-not-safe-for-const.stderr | 4 +- src/test/ui/consts/const-for.rs | 4 +- src/test/ui/consts/const-for.stderr | 13 +- .../const-mut-refs/issue-76510.32bit.stderr | 17 +- .../const-mut-refs/issue-76510.64bit.stderr | 17 +- .../ui/consts/const-mut-refs/issue-76510.rs | 1 - .../ui/consts/const-tup-index-span.stderr | 4 + .../consts/const_fn_trait_bound.stock.stderr | 6 +- .../ui/consts/control-flow/issue-46843.rs | 2 +- .../ui/consts/control-flow/issue-46843.stderr | 4 +- src/test/ui/consts/drop_box.rs | 4 + src/test/ui/consts/drop_box.stderr | 11 + .../ui/consts/intrinsic_without_const_stab.rs | 2 +- .../intrinsic_without_const_stab.stderr | 4 +- .../intrinsic_without_const_stab_fail.rs | 2 +- .../intrinsic_without_const_stab_fail.stderr | 4 +- src/test/ui/consts/issue-28113.rs | 2 +- src/test/ui/consts/issue-28113.stderr | 5 +- src/test/ui/consts/issue-32829-2.rs | 6 +- src/test/ui/consts/issue-32829-2.stderr | 12 +- src/test/ui/consts/issue-43105.rs | 2 +- src/test/ui/consts/issue-43105.stderr | 4 +- src/test/ui/consts/issue-56164.rs | 2 +- src/test/ui/consts/issue-56164.stderr | 5 +- .../issue-68542-closure-in-array-len.rs | 2 +- .../issue-68542-closure-in-array-len.stderr | 5 +- src/test/ui/consts/issue-78655.rs | 2 +- src/test/ui/consts/issue-78655.stderr | 11 +- src/test/ui/consts/issue-90870.fixed | 6 +- src/test/ui/consts/issue-90870.rs | 6 +- src/test/ui/consts/issue-90870.stderr | 9 +- src/test/ui/consts/issue-91560.fixed | 21 + src/test/ui/consts/issue-91560.rs | 21 + src/test/ui/consts/issue-91560.stderr | 21 + .../min_const_fn/bad_const_fn_body_ice.rs | 2 +- .../min_const_fn/bad_const_fn_body_ice.stderr | 3 +- .../consts/min_const_fn/min_const_fn.stderr | 24 +- .../min_const_fn/min_const_fn_dyn.stderr | 4 +- src/test/ui/consts/mir_check_nonconst.rs | 2 +- src/test/ui/consts/mir_check_nonconst.stderr | 4 +- .../ui/consts/miri_unleashed/inline_asm.rs | 13 +- .../consts/miri_unleashed/inline_asm.stderr | 20 +- src/test/ui/consts/miri_unleashed/tls.stderr | 4 +- src/test/ui/consts/recursive.rs | 11 + src/test/ui/consts/recursive.stderr | 31 + .../unstable-const-fn-in-libcore.stderr | 8 +- .../ui/derive-uninhabited-enum-38885.stderr | 6 + .../ui/derives/clone-debug-dead-code.stderr | 21 + src/test/ui/derives/issue-91550.rs | 29 + src/test/ui/derives/issue-91550.stderr | 84 + .../ui/deriving/deriving-associated-types.rs | 1 - src/test/ui/did_you_mean/bad-assoc-ty.rs | 20 +- src/test/ui/did_you_mean/bad-assoc-ty.stderr | 20 +- .../ui/did_you_mean/compatible-variants.rs | 8 + .../did_you_mean/compatible-variants.stderr | 31 +- .../issue-39802-show-5-trait-impls.stderr | 3 +- src/test/ui/did_you_mean/issue-40396.stderr | 14 +- .../issue-93210-ignore-doc-hidden.rs | 24 + .../issue-93210-ignore-doc-hidden.stderr | 19 + ...dition-keywords-2015-2018-expansion.stderr | 4 +- .../edition-keywords-2018-2015-parsing.stderr | 8 +- ...dition-keywords-2018-2018-expansion.stderr | 4 +- .../edition-keywords-2018-2018-parsing.stderr | 8 +- src/test/ui/error-codes/E0121.stderr | 4 +- src/test/ui/error-codes/E0604.stderr | 6 + src/test/ui/error-codes/E0660.rs | 10 - src/test/ui/error-codes/E0660.stderr | 15 - src/test/ui/error-codes/E0661.rs | 10 - src/test/ui/error-codes/E0661.stderr | 16 - src/test/ui/error-codes/E0662.rs | 11 - src/test/ui/error-codes/E0662.stderr | 9 - src/test/ui/error-codes/E0663.rs | 11 - src/test/ui/error-codes/E0663.stderr | 9 - src/test/ui/error-codes/E0664.rs | 12 - src/test/ui/error-codes/E0664.stderr | 9 - src/test/ui/error-festival.stderr | 6 + src/test/ui/expr/if/attrs/let-chains-attr.rs | 2 +- .../ui/expr/if/attrs/let-chains-attr.stderr | 11 - .../feature-gates/feature-gate-allow_fail.rs | 8 - .../feature-gate-allow_fail.stderr | 12 - src/test/ui/feature-gates/feature-gate-asm.rs | 10 - .../ui/feature-gates/feature-gate-asm.stderr | 12 - .../ui/feature-gates/feature-gate-asm2.rs | 10 - .../ui/feature-gates/feature-gate-asm2.stderr | 12 - .../feature-gate-asm_const.stderr | 2 +- .../feature-gate-asm_experimental_arch.stderr | 2 +- .../feature-gates/feature-gate-asm_sym.stderr | 2 +- .../feature-gate-asm_unwind.stderr | 2 +- .../feature-gate-associated_const_equality.rs | 15 + ...ture-gate-associated_const_equality.stderr | 12 + .../feature-gate-associated_type_bounds.rs | 6 +- ...feature-gate-associated_type_bounds.stderr | 6 +- .../feature-gates/feature-gate-cfg-panic.rs | 11 - .../feature-gate-cfg-panic.stderr | 21 - .../feature-gate-cfg-target-abi.rs | 4 +- .../feature-gate-cfg-target-abi.stderr | 16 +- ...e-cfg-target-has-atomic-equal-alignment.rs | 14 + ...g-target-has-atomic-equal-alignment.stderr | 57 + .../feature-gate-cfg-target-has-atomic.rs | 104 - .../feature-gate-cfg-target-has-atomic.stderr | 242 +- .../feature-gates/feature-gate-check-cfg.rs | 3 + .../feature-gate-check-cfg.stderr | 2 + .../feature-gate-in_band_lifetimes.stderr | 168 +- .../feature-gate-raw-dylib-windows-gnu.rs | 8 - .../feature-gate-raw-dylib-windows-gnu.stderr | 18 - ...dows-msvc.rs => feature-gate-raw-dylib.rs} | 3 +- ...c.stderr => feature-gate-raw-dylib.stderr} | 2 +- ...-gate-unboxed-closures-manual-impls.stderr | 54 +- .../feature-gate-unboxed-closures.stderr | 6 +- .../feature-gate-unsafe_pin_internals.rs | 17 + .../feature-gate-unsafe_pin_internals.stderr | 14 + .../feature-gate-untagged_unions.rs | 4 +- .../feature-gate-untagged_unions.stderr | 10 +- .../feature-gate-used_with_arg.rs | 7 + .../feature-gate-used_with_arg.stderr | 21 + .../feature-gate-with_negative_coherence.rs | 8 + ...eature-gate-with_negative_coherence.stderr | 12 + .../issue-43106-gating-of-builtin-attrs.rs | 47 +- ...issue-43106-gating-of-builtin-attrs.stderr | 441 +- .../format-args-capture-issue-93378.stderr | 8 +- .../ui/fmt/format-args-capture-issue-94010.rs | 7 + .../format-args-capture-issue-94010.stderr | 20 + ...rmat-args-capture-missing-variables.stderr | 24 +- src/test/ui/fmt/format-args-capture.rs | 1 - src/test/ui/fmt/format-with-yield-point.rs | 33 + src/test/ui/fmt/ifmt-bad-arg.stderr | 20 +- src/test/ui/fmt/ifmt-unimpl.stderr | 5 + ...d-bounds-unnorm-associated-type.nll.stderr | 2 +- src/test/ui/fn/issue-80179.rs | 4 +- src/test/ui/fn/issue-80179.stderr | 4 +- .../issue-91370-foreign-fn-block-impl.rs | 12 + .../issue-91370-foreign-fn-block-impl.stderr | 21 + ...n-help-with-err-generic-is-not-function.rs | 22 + ...lp-with-err-generic-is-not-function.stderr | 32 + .../ui/functions-closures/fn-help-with-err.rs | 16 + .../fn-help-with-err.stderr | 24 + src/test/ui/generator/drop-control-flow.rs | 143 + src/test/ui/generator/drop-yield-twice.rs | 15 + src/test/ui/generator/drop-yield-twice.stderr | 25 + src/test/ui/generator/issue-57478.rs | 20 + src/test/ui/generator/issue-93161.rs | 93 + src/test/ui/generator/partial-drop.rs | 44 + src/test/ui/generator/partial-drop.stderr | 71 + .../print/generator-print-verbose-1.stderr | 10 +- .../ui/generator/reinit-in-match-guard.rs | 25 + .../bugs/issue-80626.rs | 17 + .../bugs/issue-80626.stderr | 20 + .../bugs/issue-86218.rs | 27 + .../bugs/issue-86218.stderr | 15 + .../bugs/issue-87735.rs | 46 + .../bugs/issue-87735.stderr | 9 + .../bugs/issue-87748.rs | 23 + .../bugs/issue-87748.stderr | 20 + .../bugs/issue-87755.rs | 21 + .../bugs/issue-87755.stderr | 9 + .../bugs/issue-87803.rs | 27 + .../bugs/issue-87803.stderr | 12 + .../bugs/issue-88382.rs | 31 + .../bugs/issue-88382.stderr | 20 + .../bugs/issue-88460.rs | 31 + .../bugs/issue-88460.stderr | 18 + .../bugs/issue-88526.rs | 35 + .../bugs/issue-88526.stderr | 9 + .../bugs/issue-89008.rs | 44 + .../bugs/issue-89008.stderr | 21 + .../elided-in-expr-position.rs | 38 + .../elided-in-expr-position.stderr | 35 + .../impl_bounds.stderr | 7 +- .../generic-associated-types/issue-88595.rs | 5 +- .../issue-88595.stderr | 22 +- .../generic-associated-types/issue-89352.rs | 32 + .../issue-90014.stderr | 7 +- .../generic-associated-types/issue-91139.rs | 22 + .../generic-associated-types/issue-91762.rs | 30 + .../issue-91762.stderr | 9 + .../generic-associated-types/issue-92033.rs | 39 + .../issue-92033.stderr | 22 + .../issue-92096.migrate.stderr | 18 + .../generic-associated-types/issue-92096.rs | 29 + .../generic-associated-types/issue-92280.rs | 26 + .../generic-associated-types/issue-92954.rs | 10 + .../generic-associated-types/issue-93141.rs | 25 + .../generic-associated-types/issue-93340.rs | 20 + .../generic-associated-types/issue-93874.rs | 35 + .../self-outlives-lint.rs | 21 + .../self-outlives-lint.stderr | 30 +- .../hygiene/rustc-macro-transparency.stderr | 28 +- src/test/ui/impl-trait/example-calendar.rs | 1 - src/test/ui/impl-trait/issue-55872-2.rs | 1 - src/test/ui/impl-trait/issue-55872-2.stderr | 4 +- src/test/ui/impl-trait/issue-55872.rs | 1 - src/test/ui/impl-trait/issue-55872.stderr | 2 +- src/test/ui/impl-trait/issues/issue-54600.rs | 2 +- .../ui/impl-trait/issues/issue-54600.stderr | 2 +- src/test/ui/impl-trait/issues/issue-54840.rs | 2 +- .../ui/impl-trait/issues/issue-54840.stderr | 2 +- src/test/ui/impl-trait/issues/issue-58504.rs | 2 +- .../ui/impl-trait/issues/issue-58504.stderr | 2 +- src/test/ui/impl-trait/issues/issue-58956.rs | 4 +- .../ui/impl-trait/issues/issue-58956.stderr | 4 +- src/test/ui/impl-trait/issues/issue-65581.rs | 1 + src/test/ui/impl-trait/issues/issue-70971.rs | 2 +- .../ui/impl-trait/issues/issue-70971.stderr | 2 +- src/test/ui/impl-trait/issues/issue-79099.rs | 2 +- .../ui/impl-trait/issues/issue-79099.stderr | 2 +- ...sue-83929-impl-trait-in-generic-default.rs | 4 +- ...83929-impl-trait-in-generic-default.stderr | 4 +- src/test/ui/impl-trait/issues/issue-84919.rs | 2 +- .../ui/impl-trait/issues/issue-84919.stderr | 2 +- src/test/ui/impl-trait/issues/issue-86642.rs | 2 +- .../ui/impl-trait/issues/issue-86642.stderr | 2 +- src/test/ui/impl-trait/issues/issue-87295.rs | 2 +- .../ui/impl-trait/issues/issue-87295.stderr | 2 +- src/test/ui/impl-trait/nested_impl_trait.rs | 4 +- .../ui/impl-trait/nested_impl_trait.stderr | 4 +- src/test/ui/impl-trait/where-allowed.rs | 82 +- src/test/ui/impl-trait/where-allowed.stderr | 82 +- .../hrlt-implied-trait-bounds-guard.rs | 51 + .../hrlt-implied-trait-bounds-guard.stderr | 12 + .../hrlt-implied-trait-bounds-roundtrip.rs | 35 + src/test/ui/inference/char-as-str-multi.rs | 3 +- .../ui/inference/char-as-str-multi.stderr | 10 +- src/test/ui/inference/deref-suggestion.stderr | 2 +- .../ui/infinite/infinite-autoderef.stderr | 9 +- .../ui/intrinsics/const-eval-select-bad.rs | 4 +- .../intrinsics/const-eval-select-bad.stderr | 13 +- ...protection-missing-pac-ret.BADFLAGS.stderr | 2 + ...rotection-missing-pac-ret.BADTARGET.stderr | 4 + .../branch-protection-missing-pac-ret.rs | 14 + .../ui/invalid/invalid-no-sanitize.stderr | 2 +- src/test/ui/issues/issue-14772.rs | 6 - src/test/ui/issues/issue-14772.stderr | 8 - src/test/ui/issues/issue-16538.mir.stderr | 4 +- src/test/ui/issues/issue-16538.thir.stderr | 4 +- src/test/ui/issues/issue-16683.nll.stderr | 13 +- src/test/ui/issues/issue-17758.nll.stderr | 13 +- src/test/ui/issues/issue-23036.rs | 1 - src/test/ui/issues/issue-23122-1.rs | 6 +- src/test/ui/issues/issue-23122-2.rs | 5 +- src/test/ui/issues/issue-23122-2.stderr | 4 +- src/test/ui/issues/issue-25901.rs | 2 +- src/test/ui/issues/issue-25901.stderr | 15 +- src/test/ui/issues/issue-28561.rs | 1 - src/test/ui/issues/issue-3214.rs | 3 +- src/test/ui/issues/issue-3214.stderr | 10 +- src/test/ui/issues/issue-33187.rs | 16 +- src/test/ui/issues/issue-35570.stderr | 4 +- src/test/ui/issues/issue-37051.rs | 1 - src/test/ui/issues/issue-39559-2.stderr | 8 +- src/test/ui/issues/issue-47377.stderr | 5 +- src/test/ui/issues/issue-47380.stderr | 5 +- src/test/ui/issues/issue-47715.rs | 8 +- src/test/ui/issues/issue-47715.stderr | 8 +- src/test/ui/issues/issue-5100.stderr | 6 +- src/test/ui/issues/issue-52213.nll.stderr | 2 +- src/test/ui/issues/issue-55796.nll.stderr | 4 +- src/test/ui/issues/issue-55796.rs | 6 +- src/test/ui/issues/issue-55796.stderr | 20 +- .../issues/issue-68696-catch-during-unwind.rs | 1 - .../issue-69396-const-no-type-in-macro.rs | 2 +- .../issue-69396-const-no-type-in-macro.stderr | 2 +- src/test/ui/issues/issue-69455.stderr | 18 +- .../issue-74564-if-expr-stack-overflow.rs | 1 - src/test/ui/issues/issue-85461.rs | 2 +- src/test/ui/issues/issue-9129.rs | 2 - src/test/ui/iterators/collect-into-slice.rs | 15 + .../ui/iterators/collect-into-slice.stderr | 26 + .../keyword-extern-as-identifier-pat.stderr | 4 +- .../keyword-extern-as-identifier-use.stderr | 4 +- src/test/ui/kindck/kindck-copy.stderr | 8 + .../ui/lexer/lex-bad-char-literals-1.stderr | 8 + .../ui/lint/dead-code/lint-dead-code-1.stderr | 20 +- .../ui/lint/dead-code/unused-variant.stderr | 6 + .../lint-pub-unreachable-for-nested-glob.rs | 28 + .../ui/lint/lint-unconditional-recursion.rs | 42 + .../lint/lint-unconditional-recursion.stderr | 46 +- src/test/ui/lint/must_not_suspend/dedup.rs | 2 +- .../ui/lint/must_not_suspend/dedup.stderr | 12 +- .../ui/llvm-asm/asm-src-loc-codegen-units.rs | 13 - src/test/ui/llvm-asm/asm-src-loc.rs | 12 - .../ui/llvm-asm/inline-asm-bad-constraint.rs | 41 - .../llvm-asm/inline-asm-bad-constraint.stderr | 27 - .../ui/llvm-asm/inline-asm-bad-operand.rs | 60 - .../ui/llvm-asm/inline-asm-bad-operand.stderr | 45 - src/test/ui/llvm-asm/issue-14936.rs | 49 - src/test/ui/llvm-asm/issue-23458.rs | 12 - src/test/ui/llvm-asm/issue-23458.stderr | 20 - src/test/ui/llvm-asm/issue-33264.rs | 30 - src/test/ui/llvm-asm/issue-37366.rs | 16 - src/test/ui/llvm-asm/issue-37433.rs | 12 - src/test/ui/llvm-asm/issue-37433.stderr | 9 - src/test/ui/llvm-asm/issue-51431.rs | 12 - src/test/ui/llvm-asm/issue-51431.stderr | 9 - .../issue-53787-inline-assembler-macro.rs | 27 - .../issue-53787-inline-assembler-macro.stderr | 9 - src/test/ui/llvm-asm/issue-54067.rs | 13 - src/test/ui/llvm-asm/issue-62046.rs | 12 - src/test/ui/llvm-asm/issue-62046.stderr | 11 - src/test/ui/llvm-asm/issue-69092.rs | 13 - src/test/ui/llvm-asm/issue-69092.stderr | 14 - src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs | 27 - .../ui/llvm-asm/llvm-asm-bad-clobber.stderr | 9 - src/test/ui/llvm-asm/llvm-asm-concat-src.rs | 10 - .../ui/llvm-asm/llvm-asm-in-bad-modifier.rs | 36 - .../llvm-asm/llvm-asm-in-bad-modifier.stderr | 16 - src/test/ui/llvm-asm/llvm-asm-in-moved.rs | 32 - .../ui/llvm-asm/llvm-asm-in-out-operand.rs | 57 - .../ui/llvm-asm/llvm-asm-indirect-memory.rs | 44 - .../ui/llvm-asm/llvm-asm-literal-escaping.rs | 13 - .../ui/llvm-asm/llvm-asm-misplaced-option.rs | 37 - .../llvm-asm/llvm-asm-misplaced-option.stderr | 14 - .../ui/llvm-asm/llvm-asm-out-assign-imm.rs | 36 - .../llvm-asm/llvm-asm-out-assign-imm.stderr | 14 - src/test/ui/llvm-asm/llvm-asm-out-assign.rs | 26 - .../ui/llvm-asm/llvm-asm-out-no-modifier.rs | 33 - .../llvm-asm/llvm-asm-out-no-modifier.stderr | 9 - .../ui/llvm-asm/llvm-asm-out-read-uninit.rs | 34 - .../llvm-asm/llvm-asm-out-read-uninit.stderr | 9 - src/test/ui/llvm-asm/llvm-asm-parse-errors.rs | 16 - .../ui/llvm-asm/llvm-asm-parse-errors.stderr | 68 - src/test/ui/macros/macro-interpolation.rs | 14 +- src/test/ui/macros/macros-nonfatal-errors.rs | 2 - .../ui/macros/macros-nonfatal-errors.stderr | 68 +- src/test/ui/macros/stringify.rs | 22 +- src/test/ui/match/issue-82392.stdout | 17 +- .../match/match-ref-mut-invariance.nll.stderr | 2 +- .../match-ref-mut-let-invariance.nll.stderr | 2 +- src/test/ui/mir/mir_let_chains_drop_order.rs | 93 + .../ui/mismatched_types/cast-rfc0401.stderr | 6 + .../mismatched_types/overloaded-calls-bad.rs | 1 + .../overloaded-calls-bad.stderr | 8 +- src/test/ui/never_type/issue-52443.rs | 4 +- src/test/ui/never_type/issue-52443.stderr | 13 +- src/test/ui/nll/issue-52113.stderr | 2 +- src/test/ui/nll/issue-55394.nll.stderr | 2 +- src/test/ui/nll/issue-55825-const-fn.stderr | 2 +- src/test/ui/nll/issue-67007-escaping-data.rs | 2 +- .../ui/nll/issue-67007-escaping-data.stderr | 13 +- src/test/ui/nll/lint-no-err.rs | 28 + src/test/ui/nll/lint-no-err.stderr | 17 + src/test/ui/nll/mir_check_cast_closure.stderr | 2 +- .../ui/nll/outlives-suggestion-more.stderr | 6 +- src/test/ui/nll/outlives-suggestion-simple.rs | 2 +- .../ui/nll/outlives-suggestion-simple.stderr | 22 +- .../ty-outlives/impl-trait-captures.stderr | 6 +- src/test/ui/nll/ty-outlives/issue-53789-2.rs | 59 +- .../ui/nll/type-alias-free-regions.nll.stderr | 4 +- .../nll/type-check-pointer-coercions.stderr | 14 +- .../nll/user-annotations/wf-self-type.stderr | 2 +- .../ui/numeric/uppercase-base-prefix.fixed | 77 + src/test/ui/numeric/uppercase-base-prefix.rs | 77 + .../ui/numeric/uppercase-base-prefix.stderr | 98 + ...object-lifetime-default-elision.nll.stderr | 2 +- src/test/ui/occurs-check-2.stderr | 9 +- src/test/ui/occurs-check.stderr | 9 +- src/test/ui/optimization-remark.rs | 4 +- src/test/ui/panics/default-backtrace-ice.rs | 9 + .../ui/panics/default-backtrace-ice.stderr | 18 + .../panics/panic-handler-chain-update-hook.rs | 36 + .../panics/runtime-switch.legacy.run.stderr | 5 + src/test/ui/panics/runtime-switch.rs | 25 + .../ui/panics/runtime-switch.v0.run.stderr | 5 + .../parser/bad-escape-suggest-raw-string.rs | 7 + .../bad-escape-suggest-raw-string.stderr | 14 + .../ui/parser/bad-struct-following-where.rs | 2 + .../parser/bad-struct-following-where.stderr | 8 + .../ui/parser/bad-value-ident-false.stderr | 4 +- .../ui/parser/bad-value-ident-true.stderr | 4 +- src/test/ui/parser/duplicate-where-clauses.rs | 19 + .../ui/parser/duplicate-where-clauses.stderr | 80 + src/test/ui/parser/issues/issue-15980.stderr | 4 +- .../issues/issue-35813-postfix-after-cast.rs | 4 +- .../issue-35813-postfix-after-cast.stderr | 4 +- src/test/ui/parser/issues/issue-44406.stderr | 4 +- src/test/ui/parser/issues/issue-57198.stderr | 4 +- src/test/ui/parser/issues/issue-81806.stderr | 4 +- src/test/ui/parser/issues/issue-84148-1.rs | 5 +- .../ui/parser/issues/issue-84148-1.stderr | 18 +- src/test/ui/parser/issues/issue-84148-2.rs | 3 +- .../ui/parser/issues/issue-84148-2.stderr | 24 +- src/test/ui/parser/issues/issue-8537.stderr | 2 +- src/test/ui/parser/issues/issue-93282.rs | 15 + src/test/ui/parser/issues/issue-93282.stderr | 25 + src/test/ui/parser/keyword-abstract.stderr | 4 +- .../ui/parser/keyword-as-as-identifier.stderr | 4 +- .../parser/keyword-break-as-identifier.stderr | 4 +- .../parser/keyword-const-as-identifier.stderr | 4 +- .../keyword-continue-as-identifier.stderr | 4 +- .../parser/keyword-else-as-identifier.stderr | 4 +- .../parser/keyword-enum-as-identifier.stderr | 4 +- src/test/ui/parser/keyword-final.stderr | 4 +- .../ui/parser/keyword-fn-as-identifier.stderr | 4 +- .../parser/keyword-for-as-identifier.stderr | 4 +- .../ui/parser/keyword-if-as-identifier.stderr | 4 +- .../parser/keyword-impl-as-identifier.stderr | 4 +- .../parser/keyword-let-as-identifier.stderr | 4 +- .../parser/keyword-loop-as-identifier.stderr | 4 +- .../parser/keyword-match-as-identifier.stderr | 4 +- .../parser/keyword-mod-as-identifier.stderr | 4 +- .../parser/keyword-move-as-identifier.stderr | 4 +- src/test/ui/parser/keyword-override.stderr | 4 +- .../parser/keyword-pub-as-identifier.stderr | 4 +- .../keyword-return-as-identifier.stderr | 4 +- .../keyword-static-as-identifier.stderr | 4 +- .../keyword-struct-as-identifier.stderr | 4 +- .../parser/keyword-trait-as-identifier.stderr | 4 +- ...yword-try-as-identifier-edition2018.stderr | 4 +- .../parser/keyword-type-as-identifier.stderr | 4 +- src/test/ui/parser/keyword-typeof.stderr | 4 +- .../keyword-unsafe-as-identifier.stderr | 4 +- .../parser/keyword-use-as-identifier.stderr | 4 +- .../parser/keyword-where-as-identifier.stderr | 4 +- .../parser/keyword-while-as-identifier.stderr | 4 +- src/test/ui/parser/keyword.stderr | 4 +- src/test/ui/parser/macro-keyword.stderr | 4 +- src/test/ui/parser/mut-patterns.stderr | 12 +- .../parser/recover-assoc-const-constraint.rs | 6 +- .../recover-assoc-const-constraint.stderr | 23 +- .../require-parens-for-chained-comparison.rs | 26 +- ...quire-parens-for-chained-comparison.stderr | 67 +- .../parser/trailing-question-in-macro-type.rs | 14 + .../trailing-question-in-macro-type.stderr | 9 + .../ui/parser/trailing-question-in-type.fixed | 10 + .../ui/parser/trailing-question-in-type.rs | 10 + .../parser/trailing-question-in-type.stderr | 24 + src/test/ui/pattern/issue-82290.rs | 9 - src/test/ui/pattern/issue-82290.stderr | 21 - .../ui/pin-macro/cant_access_internals.rs | 13 + .../ui/pin-macro/cant_access_internals.stderr | 11 + .../lifetime_errors_on_promotion_misusage.rs | 29 + ...fetime_errors_on_promotion_misusage.stderr | 31 + src/test/ui/privacy/auxiliary/issue-92755.rs | 17 + src/test/ui/privacy/issue-92755.rs | 10 + src/test/ui/proc-macro/cfg-eval-fail.rs | 2 - src/test/ui/proc-macro/cfg-eval-fail.stderr | 14 +- src/test/ui/proc-macro/cfg-eval-inner.stdout | 8 +- .../issue-73933-procedural-masquerade.rs | 9 +- .../issue-73933-procedural-masquerade.stderr | 65 +- .../issue-73933-procedural-masquerade.stdout | 4 +- .../proc-macro/issue-75930-derive-cfg.stderr | 14 +- .../proc-macro/issue-75930-derive-cfg.stdout | 50 +- .../proc-macro/macro-rules-derive-cfg.stdout | 8 +- src/test/ui/proc-macro/quote-debug.stdout | 39 +- src/test/ui/ptr_ops/issue-80309-safe.rs | 17 + src/test/ui/ptr_ops/issue-80309.rs | 14 + src/test/ui/reachable/expr_unary.rs | 4 +- src/test/ui/reachable/expr_unary.stderr | 10 +- src/test/ui/recursion/issue-83150.rs | 2 +- src/test/ui/recursion/issue-83150.stderr | 13 +- .../region-object-lifetime-2.nll.stderr | 2 +- ...ion-object-lifetime-in-coercion.nll.stderr | 2 +- ...hod-type-parameters-trait-bound.nll.stderr | 14 +- src/test/ui/regions/regions-bounds.nll.stderr | 4 +- ...se-over-type-parameter-multiple.nll.stderr | 2 +- .../regions-creating-enums4.nll.stderr | 2 +- ...egions-early-bound-error-method.nll.stderr | 2 +- ...-free-region-ordering-incorrect.nll.stderr | 2 +- ...-implied-bounds-projection-gap-hr-1.stderr | 4 +- .../regions-infer-not-param.nll.stderr | 4 +- .../regions-trait-object-subtyping.nll.stderr | 4 +- src/test/ui/reserved/reserved-become.stderr | 4 +- src/test/ui/resolve/resolve-hint-macro.fixed | 11 + src/test/ui/resolve/resolve-hint-macro.rs | 7 + src/test/ui/resolve/resolve-hint-macro.stderr | 45 +- src/test/ui/return/return-impl-trait-bad.rs | 31 + .../ui/return/return-impl-trait-bad.stderr | 59 + src/test/ui/return/return-impl-trait.fixed | 30 + src/test/ui/return/return-impl-trait.rs | 30 + src/test/ui/return/return-impl-trait.stderr | 34 + .../return/tail-expr-as-potential-return.rs | 22 + .../tail-expr-as-potential-return.stderr | 22 +- .../ui/rfc-2294-if-let-guard/feature-gate.rs | 37 +- .../rfc-2294-if-let-guard/feature-gate.stderr | 120 +- .../dbg-macro-expected-behavior.run.stderr | 2 +- .../ast-lowering-does-not-wrap-let-chains.rs | 15 + .../ast-pretty-check.rs | 2 +- .../ast-pretty-check.stdout | 2 +- .../chains-without-let.rs | 20 + .../chains-without-let.stderr | 21 + .../disallowed-positions.rs | 1 - .../disallowed-positions.stderr | 221 +- .../ui/rfc-2497-if-let-chains/feature-gate.rs | 64 +- .../feature-gate.stderr | 96 +- .../irrefutable-lets.rs | 37 + .../ui/rfc-2497-if-let-chains/issue-88498.rs | 16 + .../ui/rfc-2497-if-let-chains/issue-90722.rs | 11 + .../ui/rfc-2497-if-let-chains/issue-92145.rs | 11 + .../ui/rfc-2497-if-let-chains/issue-93150.rs | 8 + .../rfc-2497-if-let-chains/issue-93150.stderr | 22 + .../no-double-assigments.rs | 9 + .../then-else-blocks.rs | 49 + .../auxiliary/param-attrs.rs | 4 +- .../link-ordinal-multiple.rs | 2 +- .../multiple-declarations.rs | 3 +- .../multiple-declarations.stderr | 4 +- .../rfc-2627-raw-dylib/raw-dylib-msvc-only.rs | 8 - .../raw-dylib-msvc-only.stderr | 17 - .../ui/rfc-2627-raw-dylib/unsupported-abi.rs | 3 +- .../rfc-2627-raw-dylib/unsupported-abi.stderr | 2 +- .../rfc-2632-const-trait-impl/assoc-type.rs | 4 +- .../assoc-type.stderr | 13 +- .../call-const-trait-method-fail.rs | 3 +- .../call-const-trait-method-fail.stderr | 25 +- .../call-generic-method-fail.rs | 3 +- .../call-generic-method-fail.stderr | 25 +- .../call-generic-method-nonconst.stderr | 9 +- .../const-check-fns-in-const-impl.rs | 2 +- .../const-check-fns-in-const-impl.stderr | 4 +- .../const-default-method-bodies.rs | 3 +- .../const-default-method-bodies.stderr | 25 +- .../const-drop-fail.precise.stderr | 78 +- .../const-drop-fail.rs | 4 +- .../const-drop-fail.stock.stderr | 78 +- .../rfc-2632-const-trait-impl/const-drop.rs | 31 + .../cross-crate.gated.stderr | 25 +- .../rfc-2632-const-trait-impl/cross-crate.rs | 6 +- .../cross-crate.stock.stderr | 16 +- ...ault-method-body-is-const-body-checking.rs | 2 +- ...-method-body-is-const-body-checking.stderr | 13 +- ...ault-method-body-is-const-same-trait-ck.rs | 18 + ...-method-body-is-const-same-trait-ck.stderr | 24 + .../rfc-2632-const-trait-impl/issue-88155.rs | 3 +- .../issue-88155.stderr | 21 +- .../std-impl-gate.rs | 2 +- .../std-impl-gate.stock.stderr | 4 +- .../trait-where-clause.stderr | 26 +- src/test/ui/runtime/out-of-stack.rs | 6 +- .../rust-2018/dyn-trait-compatibility.stderr | 4 +- .../edition-lint-nested-empty-paths.fixed | 14 + .../edition-lint-nested-empty-paths.rs | 14 + .../edition-lint-nested-empty-paths.stderr | 69 +- .../rust-2018/edition-lint-nested-paths.fixed | 10 + .../ui/rust-2018/edition-lint-nested-paths.rs | 10 + .../edition-lint-nested-paths.stderr | 49 +- .../ui/rust-2018/edition-lint-paths.fixed | 13 + src/test/ui/rust-2018/edition-lint-paths.rs | 13 + .../ui/rust-2018/edition-lint-paths.stderr | 59 +- .../ui/rust-2018/extern-crate-rename.fixed | 2 + src/test/ui/rust-2018/extern-crate-rename.rs | 2 + .../ui/rust-2018/extern-crate-rename.stderr | 11 +- .../ui/rust-2018/extern-crate-submod.fixed | 3 + src/test/ui/rust-2018/extern-crate-submod.rs | 3 + .../ui/rust-2018/extern-crate-submod.stderr | 11 +- src/test/ui/save-analysis/issue-89066.rs | 28 + src/test/ui/save-analysis/issue-89066.stderr | 39 + ...f_types_pin_lifetime_mismatch-async.stderr | 21 +- .../ui/self/elision/lt-ref-self-async.stderr | 54 +- .../ui/self/elision/ref-mut-self-async.stderr | 54 +- .../self/elision/ref-mut-struct-async.stderr | 45 +- .../ui/self/elision/ref-self-async.stderr | 63 +- .../ui/self/elision/ref-struct-async.stderr | 45 +- src/test/ui/self/self-infer.rs | 4 +- src/test/ui/self/self-infer.stderr | 4 +- src/test/ui/simd/intrinsic/generic-as.rs | 48 + .../intrinsic/generic-cast-pointer-width.rs | 21 + src/test/ui/simd/libm_std_can_float.rs | 23 + .../simd/portable-intrinsics-arent-exposed.rs | 3 +- .../portable-intrinsics-arent-exposed.stderr | 23 +- src/test/ui/span/coerce-suggestions.stderr | 9 +- src/test/ui/span/impl-wrong-item-for-trait.rs | 8 +- .../ui/span/impl-wrong-item-for-trait.stderr | 50 +- src/test/ui/span/issue-39018.stderr | 53 +- ....rs => default-associated-type-bound-1.rs} | 1 - ...=> default-associated-type-bound-1.stderr} | 8 +- ....rs => default-associated-type-bound-2.rs} | 0 ...=> default-associated-type-bound-2.stderr} | 6 +- ... default-generic-associated-type-bound.rs} | 0 ...ault-generic-associated-type-bound.stderr} | 6 +- .../repeated_projection_type.stderr | 2 +- .../generics-default-stability-trait.rs | 33 + .../generics-default-stability-trait.stderr | 27 + .../generics-default-stability.rs | 12 - .../generics-default-stability.stderr | 162 +- .../missing-const-stability.rs | 26 +- .../missing-const-stability.stderr | 10 +- .../static-vec-repeat-not-constant.stderr | 4 +- .../ui/str/str-concat-on-double-ref.stderr | 5 +- .../args-instead-of-tuple-errors.rs | 22 + .../args-instead-of-tuple-errors.stderr | 52 + .../suggestions/args-instead-of-tuple.fixed | 33 + .../ui/suggestions/args-instead-of-tuple.rs | 33 + .../suggestions/args-instead-of-tuple.stderr | 111 + .../ui/suggestions/boxed-variant-field.rs | 2 +- .../ui/suggestions/boxed-variant-field.stderr | 2 +- ...atic-bound-needing-more-suggestions.stderr | 3 +- ...yn-trait-with-implicit-static-bound.stderr | 3 +- src/test/ui/suggestions/into-str.stderr | 6 + .../issue-71394-no-from-impl.stderr | 3 + src/test/ui/suggestions/issue-82566-1.stderr | 6 +- src/test/ui/suggestions/issue-82566-2.stderr | 6 +- .../suggestions/issue-84973-negative.stderr | 2 + .../issue-86100-tuple-paren-comma.stderr | 6 +- src/test/ui/suggestions/private-field.rs | 19 + src/test/ui/suggestions/private-field.stderr | 11 + .../raw-name-use-suggestion.stderr | 8 +- ...type-ascription-instead-of-path-in-type.rs | 14 + ...-ascription-instead-of-path-in-type.stderr | 46 + src/test/ui/suggestions/unnamable-types.rs | 4 +- .../ui/suggestions/unnamable-types.stderr | 4 +- src/test/ui/symbol-names/basic.legacy.stderr | 4 +- src/test/ui/symbol-names/basic.v0.stderr | 2 +- src/test/ui/symbol-names/foreign-types.rs | 19 + src/test/ui/symbol-names/foreign-types.stderr | 20 + src/test/ui/symbol-names/impl1.legacy.stderr | 12 +- src/test/ui/symbol-names/impl1.v0.stderr | 6 +- .../ui/symbol-names/issue-60925.legacy.stderr | 4 +- .../ui/symbol-names/issue-60925.v0.stderr | 2 +- .../ui/symbol-names/issue-75326.legacy.stderr | 4 +- .../ui/symbol-names/issue-75326.v0.stderr | 2 +- .../ui/symbol-names/trait-objects.v0.stderr | 6 +- src/test/ui/symbol-names/verbose.rs | 15 + .../tied-features-cli.one.stderr | 4 + .../ui/target-feature/tied-features-cli.rs | 9 + .../tied-features-cli.three.stderr | 4 + .../tied-features-cli.two.stderr | 4 + src/test/ui/target-feature/tied-features.rs | 29 + .../ui/target-feature/tied-features.stderr | 18 + ...non-1-width-unicode-multiline-label.stderr | 5 +- src/test/ui/terr-sorts.stderr | 2 +- .../ui/test-attrs/test-allow-fail-attr.rs | 17 - src/test/ui/test-attrs/test-on-macro.rs | 13 - src/test/ui/test-attrs/test-on-macro.stderr | 8 - src/test/ui/test-attrs/test-on-not-fn.rs | 80 + src/test/ui/test-attrs/test-on-not-fn.stderr | 173 + src/test/ui/thir-tree.stdout | 4 +- src/test/ui/trait-bounds/issue-93008.rs | 10 + src/test/ui/trait-bounds/issue-93008.stderr | 12 + .../check-trait-object-bounds-1.stderr | 2 + .../check-trait-object-bounds-4.stderr | 2 + .../rustc_must_implement_one_of.rs | 44 + .../rustc_must_implement_one_of.stderr | 15 + .../rustc_must_implement_one_of_duplicates.rs | 19 + ...tc_must_implement_one_of_duplicates.stderr | 34 + .../rustc_must_implement_one_of_gated.rs | 13 + .../rustc_must_implement_one_of_gated.stderr | 11 + .../rustc_must_implement_one_of_misuse.rs | 46 + .../rustc_must_implement_one_of_misuse.stderr | 100 + src/test/ui/traits/issue-77982.stderr | 1 + src/test/ui/traits/issue-79458.stderr | 2 + src/test/ui/traits/issue-91594.rs | 17 + src/test/ui/traits/issue-91594.stderr | 17 + .../negative-impls/auxiliary/foreign_trait.rs | 1 + .../explicitly-unimplemented-error-message.rs | 8 +- ...licitly-unimplemented-error-message.stderr | 8 +- .../rely-on-negative-impl-in-coherence.rs | 9 +- src/test/ui/traits/pointee-deduction.rs | 22 + .../suggest-deferences/issue-62530.stderr | 2 + .../ui/transmute/transmute-imut-to-mut.rs | 2 +- .../ui/transmute/transmute-imut-to-mut.stderr | 2 +- .../ui/try-trait/bad-interconversion.stderr | 3 + src/test/ui/tuple/array-diagnostics.rs | 7 + src/test/ui/tuple/array-diagnostics.stderr | 9 + src/test/ui/tuple/wrong_argument_ice-2.rs | 17 + src/test/ui/tuple/wrong_argument_ice-2.stderr | 19 + src/test/ui/tuple/wrong_argument_ice-3.rs | 17 + src/test/ui/tuple/wrong_argument_ice-3.stderr | 17 + .../type-alias-impl-trait/bound_reduction2.rs | 4 - .../bound_reduction2.stderr | 56 +- .../ui/type-alias-impl-trait/issue-53598.rs | 1 - .../type-alias-impl-trait/issue-53598.stderr | 2 +- .../issue-57611-trait-alias.nll.stderr | 26 +- .../ui/type-alias-impl-trait/issue-57700.rs | 1 - .../type-alias-impl-trait/issue-57700.stderr | 2 +- .../ui/type-alias-impl-trait/issue-60371.rs | 2 - .../type-alias-impl-trait/issue-60371.stderr | 6 +- .../type-alias-impl-trait/issue-60662.stdout | 4 +- .../ui/type-alias-impl-trait/issue-77179.rs | 2 +- .../type-alias-impl-trait/issue-77179.stderr | 2 +- .../type-alias-impl-trait-fn-type.rs | 2 +- .../type-alias-impl-trait-fn-type.stderr | 2 +- src/test/ui/typeck/issue-74086.rs | 2 +- src/test/ui/typeck/issue-74086.stderr | 2 +- src/test/ui/typeck/issue-75883.rs | 4 +- src/test/ui/typeck/issue-75883.stderr | 4 +- src/test/ui/typeck/issue-75889.stderr | 4 +- src/test/ui/typeck/issue-80779.rs | 4 +- src/test/ui/typeck/issue-80779.stderr | 4 +- src/test/ui/typeck/issue-81885.rs | 4 +- src/test/ui/typeck/issue-81885.stderr | 4 +- ...-83621-placeholder-static-in-extern.stderr | 2 +- src/test/ui/typeck/issue-91328.fixed | 47 + src/test/ui/typeck/issue-91328.rs | 47 + src/test/ui/typeck/issue-91328.stderr | 39 + .../ui/typeck/issue-91450-inner-ty-error.rs | 2 +- .../typeck/issue-91450-inner-ty-error.stderr | 2 +- src/test/ui/typeck/issue-93486.rs | 6 + src/test/ui/typeck/issue-93486.stderr | 11 + .../ui/typeck/type-placeholder-fn-in-const.rs | 6 +- .../type-placeholder-fn-in-const.stderr | 6 +- ...efault-trait-impl-cross-crate-coherence.rs | 1 + ...lt-trait-impl-cross-crate-coherence.stderr | 8 +- .../ui/typeck/typeck_type_placeholder_item.rs | 120 +- .../typeck_type_placeholder_item.stderr | 120 +- .../typeck_type_placeholder_item_help.rs | 12 +- .../typeck_type_placeholder_item_help.stderr | 12 +- src/test/ui/union/issue-41073.rs | 2 +- src/test/ui/union/issue-41073.stderr | 5 +- src/test/ui/union/union-custom-drop.rs | 2 +- src/test/ui/union/union-custom-drop.stderr | 5 +- .../union-derive-clone.mirunsafeck.stderr | 4 +- .../union-derive-clone.thirunsafeck.stderr | 4 +- .../union-with-drop-fields.mirunsafeck.stderr | 15 +- src/test/ui/union/union-with-drop-fields.rs | 6 +- ...union-with-drop-fields.thirunsafeck.stderr | 15 +- src/test/ui/unsafe/inline_asm.mir.stderr | 13 +- src/test/ui/unsafe/inline_asm.rs | 4 - src/test/ui/unsafe/inline_asm.thir.stderr | 13 +- ...riance-contravariant-arg-object.nll.stderr | 4 +- .../variance-covariant-arg-object.nll.stderr | 4 +- .../variance-invariant-arg-object.nll.stderr | 4 +- ...ance-use-contravariant-struct-1.nll.stderr | 2 +- ...variance-use-covariant-struct-1.nll.stderr | 2 +- ...variance-use-invariant-struct-1.nll.stderr | 4 +- src/test/ui/wf/wf-static-method.nll.stderr | 10 +- src/tools/build-manifest/Cargo.toml | 4 +- src/tools/build-manifest/README.md | 3 +- src/tools/build-manifest/src/main.rs | 103 +- src/tools/build-manifest/src/versions.rs | 8 +- src/tools/clippy/.cargo/config.toml | 5 +- src/tools/clippy/.github/workflows/clippy.yml | 16 +- .../clippy/.github/workflows/clippy_bors.yml | 21 +- src/tools/clippy/CHANGELOG.md | 188 +- src/tools/clippy/COPYRIGHT | 2 +- src/tools/clippy/Cargo.toml | 7 +- src/tools/clippy/LICENSE-APACHE | 2 +- src/tools/clippy/LICENSE-MIT | 2 +- src/tools/clippy/README.md | 24 +- src/tools/clippy/clippy_dev/src/bless.rs | 70 +- src/tools/clippy/clippy_dev/src/fmt.rs | 32 +- src/tools/clippy/clippy_dev/src/lint.rs | 1 - .../clippy/clippy_dev/src/update_lints.rs | 2 +- src/tools/clippy/clippy_lints/Cargo.toml | 5 +- .../clippy/clippy_lints/src/approx_const.rs | 2 +- .../clippy/clippy_lints/src/as_conversions.rs | 4 +- .../src/assertions_on_constants.rs | 120 +- .../clippy/clippy_lints/src/assign_ops.rs | 8 +- src/tools/clippy/clippy_lints/src/attrs.rs | 18 +- src/tools/clippy/clippy_lints/src/bit_mask.rs | 24 +- .../src/blocks_in_if_conditions.rs | 16 +- .../src/bool_assert_comparison.rs | 75 +- src/tools/clippy/clippy_lints/src/booleans.rs | 15 +- .../clippy/clippy_lints/src/borrow_as_ptr.rs | 99 + .../clippy/clippy_lints/src/bytecount.rs | 6 +- ...se_sensitive_file_extension_comparisons.rs | 6 +- .../src/casts/cast_possible_truncation.rs | 6 +- .../src/casts/cast_ptr_alignment.rs | 4 +- .../clippy_lints/src/casts/cast_ref_to_mut.rs | 2 +- .../clippy_lints/src/casts/cast_sign_loss.rs | 4 +- .../clippy_lints/src/casts/char_lit_as_u8.rs | 2 +- .../clippy_lints/src/casts/ptr_as_ptr.rs | 2 +- .../src/casts/unnecessary_cast.rs | 11 +- .../clippy_lints/src/cognitive_complexity.rs | 8 +- src/tools/clippy/clippy_lints/src/copies.rs | 51 +- src/tools/clippy/clippy_lints/src/default.rs | 6 +- .../src/default_numeric_fallback.rs | 21 +- .../src/default_union_representation.rs | 105 + .../clippy/clippy_lints/src/dereference.rs | 218 +- src/tools/clippy/clippy_lints/src/derive.rs | 10 +- .../clippy_lints/src/disallowed_methods.rs | 36 +- .../src/disallowed_script_idents.rs | 4 +- .../clippy_lints/src/disallowed_types.rs | 7 +- src/tools/clippy/clippy_lints/src/doc.rs | 43 +- .../clippy_lints/src/duration_subsec.rs | 2 +- .../clippy_lints/src/else_if_without_else.rs | 4 +- src/tools/clippy/clippy_lints/src/entry.rs | 22 +- .../clippy/clippy_lints/src/enum_variants.rs | 3 + src/tools/clippy/clippy_lints/src/eq_op.rs | 112 +- .../clippy_lints/src/equatable_if_let.rs | 2 +- .../clippy/clippy_lints/src/erasing_op.rs | 32 +- src/tools/clippy/clippy_lints/src/escape.rs | 5 +- .../clippy/clippy_lints/src/eta_reduction.rs | 7 +- .../clippy_lints/src/eval_order_dependence.rs | 13 +- src/tools/clippy/clippy_lints/src/exit.rs | 5 +- .../clippy/clippy_lints/src/explicit_write.rs | 49 +- .../clippy_lints/src/fallible_impl_from.rs | 24 +- .../src/floating_point_arithmetic.rs | 18 +- src/tools/clippy/clippy_lints/src/format.rs | 33 +- .../clippy/clippy_lints/src/format_args.rs | 57 +- .../clippy/clippy_lints/src/formatting.rs | 18 +- .../clippy/clippy_lints/src/from_over_into.rs | 4 +- .../clippy_lints/src/from_str_radix_10.rs | 2 +- .../clippy_lints/src/functions/must_use.rs | 17 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 27 +- .../src/functions/result_unit_err.rs | 8 +- .../src/functions/too_many_arguments.rs | 12 +- .../src/functions/too_many_lines.rs | 4 +- .../clippy_lints/src/get_last_with_len.rs | 4 +- .../clippy/clippy_lints/src/if_let_mutex.rs | 17 +- .../src/if_then_some_else_none.rs | 2 +- .../clippy_lints/src/implicit_hasher.rs | 25 +- .../clippy_lints/src/implicit_return.rs | 4 +- .../src/inconsistent_struct_constructor.rs | 4 +- .../clippy_lints/src/index_refutable_slice.rs | 28 +- .../clippy/clippy_lints/src/infinite_iter.rs | 16 +- .../clippy_lints/src/inherent_to_string.rs | 2 +- .../clippy_lints/src/init_numbered_fields.rs | 3 +- .../src/items_after_statements.rs | 6 +- .../src/iter_not_returning_iterator.rs | 67 +- .../clippy_lints/src/large_const_arrays.rs | 4 +- .../clippy_lints/src/large_stack_arrays.rs | 4 +- src/tools/clippy/clippy_lints/src/len_zero.rs | 22 +- .../clippy/clippy_lints/src/let_underscore.rs | 2 +- .../clippy_lints/src/lib.register_all.rs | 6 +- .../src/lib.register_complexity.rs | 1 - .../clippy_lints/src/lib.register_lints.rs | 35 +- .../clippy_lints/src/lib.register_nursery.rs | 4 +- .../clippy_lints/src/lib.register_pedantic.rs | 1 + .../clippy_lints/src/lib.register_perf.rs | 2 +- .../src/lib.register_restriction.rs | 2 + .../clippy_lints/src/lib.register_style.rs | 3 + src/tools/clippy/clippy_lints/src/lib.rs | 28 +- .../clippy/clippy_lints/src/lifetimes.rs | 74 +- .../src/literal_representation.rs | 6 +- .../clippy_lints/src/loops/empty_loop.rs | 2 +- .../src/loops/explicit_counter_loop.rs | 4 +- .../src/loops/explicit_into_iter_loop.rs | 5 +- .../src/loops/explicit_iter_loop.rs | 4 +- .../clippy_lints/src/loops/manual_memcpy.rs | 41 +- .../clippy/clippy_lints/src/loops/mod.rs | 2 +- .../clippy_lints/src/loops/mut_range_bound.rs | 11 +- .../src/loops/needless_collect.rs | 33 +- .../src/loops/needless_range_loop.rs | 20 +- .../clippy_lints/src/loops/never_loop.rs | 7 +- .../clippy_lints/src/loops/same_item_push.rs | 13 +- .../src/loops/single_element_loop.rs | 24 +- .../clippy/clippy_lints/src/loops/utils.rs | 40 +- .../src/loops/while_immutable_condition.rs | 15 +- .../clippy_lints/src/loops/while_let_loop.rs | 2 +- .../src/loops/while_let_on_iterator.rs | 69 +- .../clippy/clippy_lints/src/macro_use.rs | 15 +- .../clippy/clippy_lints/src/manual_assert.rs | 70 +- .../clippy_lints/src/manual_async_fn.rs | 4 +- .../clippy/clippy_lints/src/manual_bits.rs | 109 + .../clippy/clippy_lints/src/manual_map.rs | 17 +- .../clippy_lints/src/manual_non_exhaustive.rs | 6 +- .../clippy/clippy_lints/src/manual_ok_or.rs | 4 +- .../clippy/clippy_lints/src/manual_strip.rs | 14 +- .../clippy_lints/src/manual_unwrap_or.rs | 2 +- .../clippy/clippy_lints/src/map_clone.rs | 70 +- .../clippy/clippy_lints/src/map_err_ignore.rs | 2 +- .../clippy/clippy_lints/src/map_unit_fn.rs | 2 +- .../clippy_lints/src/match_result_ok.rs | 4 +- .../src/match_str_case_mismatch.rs | 14 +- src/tools/clippy/clippy_lints/src/matches.rs | 2357 ------ .../matches/infalliable_detructuring_match.rs | 44 + .../clippy_lints/src/matches/match_as_ref.rs | 85 + .../clippy_lints/src/matches/match_bool.rs | 75 + .../src/matches/match_like_matches.rs | 166 + .../src/matches/match_ref_pats.rs | 66 + .../src/matches/match_same_arms.rs | 111 + .../src/matches/match_single_binding.rs | 166 + .../src/matches/match_wild_enum.rs | 198 + .../src/matches/match_wild_err_arm.rs | 51 + .../clippy/clippy_lints/src/matches/mod.rs | 646 ++ .../src/matches/overlapping_arms.rs | 181 + .../src/matches/redundant_pattern_match.rs | 436 ++ .../matches/rest_pat_in_fully_bound_struct.rs | 29 + .../clippy_lints/src/matches/single_match.rs | 269 + .../src/matches/wild_in_or_pats.rs | 24 + .../clippy/clippy_lints/src/mem_replace.rs | 2 +- .../src/methods/bind_instead_of_map.rs | 4 +- .../clippy_lints/src/methods/chars_cmp.rs | 15 +- .../src/methods/chars_cmp_with_unwrap.rs | 2 +- .../clippy_lints/src/methods/clone_on_copy.rs | 4 +- .../src/methods/cloned_instead_of_copied.rs | 2 +- .../src/methods/expect_fun_call.rs | 29 +- .../src/methods/extend_with_drain.rs | 2 +- .../clippy_lints/src/methods/filter_map.rs | 9 +- .../methods/from_iter_instead_of_collect.rs | 2 +- .../src/methods/implicit_clone.rs | 24 +- .../src/methods/iter_overeager_cloned.rs | 62 + .../methods/manual_saturating_arithmetic.rs | 8 +- .../src/methods/manual_str_repeat.rs | 6 +- .../clippy/clippy_lints/src/methods/mod.rs | 187 +- .../src/methods/option_as_ref_deref.rs | 2 +- .../src/methods/option_map_or_none.rs | 13 +- .../src/methods/option_map_unwrap_or.rs | 19 +- .../clippy_lints/src/methods/or_fun_call.rs | 26 +- .../clippy_lints/src/methods/str_splitn.rs | 18 +- .../src/methods/unnecessary_filter_map.rs | 15 +- .../src/methods/unnecessary_iter_cloned.rs | 42 +- .../src/methods/unnecessary_to_owned.rs | 53 +- .../clippy_lints/src/methods/useless_asref.rs | 4 +- .../clippy/clippy_lints/src/methods/utils.rs | 6 +- .../src/methods/wrong_self_convention.rs | 8 +- .../clippy_lints/src/methods/zst_offset.rs | 2 +- src/tools/clippy/clippy_lints/src/minmax.rs | 2 +- src/tools/clippy/clippy_lints/src/misc.rs | 32 +- .../clippy/clippy_lints/src/misc_early/mod.rs | 4 +- .../clippy_lints/src/missing_const_for_fn.rs | 4 +- .../src/missing_enforced_import_rename.rs | 2 +- .../clippy/clippy_lints/src/module_style.rs | 4 +- .../clippy_lints/src/modulo_arithmetic.rs | 4 +- src/tools/clippy/clippy_lints/src/mut_key.rs | 4 +- src/tools/clippy/clippy_lints/src/mut_mut.rs | 6 - .../clippy/clippy_lints/src/mut_mutex_lock.rs | 6 +- .../clippy/clippy_lints/src/mut_reference.rs | 2 +- .../src/mutable_debug_assertion.rs | 52 +- .../clippy/clippy_lints/src/mutex_atomic.rs | 2 +- .../clippy_lints/src/needless_for_each.rs | 15 +- .../clippy_lints/src/needless_late_init.rs | 2 +- .../src/needless_option_as_deref.rs | 5 +- .../src/needless_pass_by_value.rs | 2 +- .../src/needless_question_mark.rs | 26 +- .../clippy_lints/src/new_without_default.rs | 5 +- .../clippy/clippy_lints/src/no_effect.rs | 4 +- .../clippy/clippy_lints/src/non_copy_const.rs | 19 +- .../clippy_lints/src/non_expressive_names.rs | 6 +- .../src/non_octal_unix_permissions.rs | 4 +- .../src/non_send_fields_in_send_ty.rs | 10 +- .../clippy/clippy_lints/src/octal_escapes.rs | 10 +- .../clippy/clippy_lints/src/open_options.rs | 4 +- .../clippy_lints/src/option_if_let_else.rs | 2 +- .../clippy_lints/src/panic_in_result_fn.rs | 32 +- .../clippy_lints/src/panic_unimplemented.rs | 56 +- .../clippy_lints/src/pass_by_ref_or_value.rs | 4 +- .../src/path_buf_push_overwrite.rs | 2 +- src/tools/clippy/clippy_lints/src/ptr.rs | 623 +- src/tools/clippy/clippy_lints/src/ptr_eq.rs | 2 +- .../clippy_lints/src/ptr_offset_with_cast.rs | 2 +- .../clippy/clippy_lints/src/question_mark.rs | 2 +- src/tools/clippy/clippy_lints/src/ranges.rs | 34 +- .../clippy_lints/src/redundant_clone.rs | 22 +- .../src/redundant_closure_call.rs | 12 +- .../clippy/clippy_lints/src/redundant_else.rs | 4 +- .../clippy_lints/src/redundant_field_names.rs | 4 +- .../clippy_lints/src/redundant_slicing.rs | 5 +- .../clippy/clippy_lints/src/reference.rs | 58 +- .../clippy/clippy_lints/src/repeat_once.rs | 2 +- .../src/return_self_not_must_use.rs | 38 +- src/tools/clippy/clippy_lints/src/returns.rs | 11 +- .../clippy_lints/src/same_name_method.rs | 2 +- .../src/self_named_constructors.rs | 6 +- .../src/semicolon_if_nothing_returned.rs | 2 +- .../src/single_char_lifetime_names.rs | 63 + .../src/single_component_path_imports.rs | 4 +- .../src/size_of_in_element_count.rs | 15 +- .../src/slow_vector_initialization.rs | 15 +- .../clippy_lints/src/stable_sort_primitive.rs | 2 +- src/tools/clippy/clippy_lints/src/strings.rs | 14 +- .../clippy_lints/src/strlen_on_c_strings.rs | 4 +- .../src/suspicious_operation_groupings.rs | 5 +- .../clippy_lints/src/suspicious_trait_impl.rs | 11 +- src/tools/clippy/clippy_lints/src/swap.rs | 4 +- .../clippy_lints/src/to_digit_is_some.rs | 4 +- .../clippy_lints/src/to_string_in_display.rs | 2 +- .../clippy_lints/src/trailing_empty_array.rs | 5 +- .../clippy/clippy_lints/src/trait_bounds.rs | 112 +- .../clippy/clippy_lints/src/transmute/mod.rs | 60 +- .../src/transmute/transmute_float_to_int.rs | 29 +- .../src/transmute/transmute_int_to_bool.rs | 4 +- .../src/transmute/transmute_int_to_char.rs | 4 +- .../src/transmute/transmute_int_to_float.rs | 4 +- .../src/transmute/transmute_num_to_bytes.rs | 4 +- .../src/transmute/transmute_ptr_to_ptr.rs | 4 +- .../src/transmute/transmute_ptr_to_ref.rs | 6 +- .../src/transmute/transmute_ref_to_ref.rs | 10 +- .../src/transmute/transmute_undefined_repr.rs | 293 + .../transmutes_expressible_as_ptr_casts.rs | 4 +- .../transmute/unsound_collection_transmute.rs | 30 +- .../src/transmute/useless_transmute.rs | 8 +- .../clippy_lints/src/types/box_collection.rs | 27 +- .../clippy/clippy_lints/src/types/mod.rs | 19 +- .../clippy_lints/src/types/option_option.rs | 30 +- .../clippy_lints/src/types/rc_buffer.rs | 37 +- .../clippy/clippy_lints/src/types/rc_mutex.rs | 6 +- .../src/types/redundant_allocation.rs | 21 +- .../clippy_lints/src/types/type_complexity.rs | 8 +- .../src/undocumented_unsafe_blocks.rs | 11 +- .../src/undropped_manually_drops.rs | 2 +- .../clippy/clippy_lints/src/uninit_vec.rs | 8 +- .../clippy/clippy_lints/src/unit_hash.rs | 4 +- .../src/unit_return_expecting_ord.rs | 11 +- .../clippy_lints/src/unit_types/unit_arg.rs | 2 +- .../clippy_lints/src/unit_types/unit_cmp.rs | 40 +- .../clippy_lints/src/unnecessary_sort_by.rs | 14 +- .../clippy_lints/src/unnested_or_patterns.rs | 4 +- .../clippy/clippy_lints/src/unused_async.rs | 10 +- .../clippy_lints/src/unused_io_amount.rs | 95 +- .../clippy/clippy_lints/src/unused_self.rs | 2 +- src/tools/clippy/clippy_lints/src/unwrap.rs | 21 +- .../clippy_lints/src/unwrap_in_result.rs | 9 +- src/tools/clippy/clippy_lints/src/use_self.rs | 24 +- .../clippy/clippy_lints/src/utils/author.rs | 10 +- .../clippy/clippy_lints/src/utils/conf.rs | 32 +- .../clippy_lints/src/utils/inspector.rs | 22 +- .../clippy_lints/src/utils/internal_lints.rs | 58 +- .../internal_lints/metadata_collector.rs | 50 +- .../clippy/clippy_lints/src/utils/mod.rs | 2 +- .../clippy_lints/src/vec_init_then_push.rs | 4 +- .../clippy_lints/src/vec_resize_to_zero.rs | 2 +- .../clippy_lints/src/verbose_file_reads.rs | 4 +- src/tools/clippy/clippy_lints/src/write.rs | 8 +- .../clippy_lints/src/zero_sized_map_values.rs | 6 +- src/tools/clippy/clippy_utils/Cargo.toml | 6 +- .../clippy/clippy_utils/src/ast_utils.rs | 46 +- src/tools/clippy/clippy_utils/src/consts.rs | 37 +- .../clippy/clippy_utils/src/diagnostics.rs | 2 +- .../clippy/clippy_utils/src/eager_or_lazy.rs | 23 +- src/tools/clippy/clippy_utils/src/higher.rs | 306 +- .../clippy/clippy_utils/src/hir_utils.rs | 35 +- src/tools/clippy/clippy_utils/src/lib.rs | 444 +- src/tools/clippy/clippy_utils/src/macros.rs | 556 ++ src/tools/clippy/clippy_utils/src/msrvs.rs | 3 +- .../clippy_utils/src/numeric_literal.rs | 9 +- src/tools/clippy/clippy_utils/src/paths.rs | 44 +- src/tools/clippy/clippy_utils/src/ptr.rs | 2 +- .../clippy_utils/src/qualify_min_const_fn.rs | 31 +- src/tools/clippy/clippy_utils/src/source.rs | 2 +- src/tools/clippy/clippy_utils/src/sugg.rs | 14 +- src/tools/clippy/clippy_utils/src/ty.rs | 145 +- src/tools/clippy/clippy_utils/src/usage.rs | 14 +- src/tools/clippy/clippy_utils/src/visitors.rs | 40 +- .../clippy/doc/common_tools_writing_lints.md | 16 +- src/tools/clippy/lintcheck/Cargo.toml | 2 +- src/tools/clippy/lintcheck/src/main.rs | 12 +- src/tools/clippy/rust-toolchain | 2 +- src/tools/clippy/rustc_tools_util/README.md | 2 +- src/tools/clippy/src/driver.rs | 3 +- src/tools/clippy/src/main.rs | 2 +- src/tools/clippy/tests/cargo/mod.rs | 4 - src/tools/clippy/tests/compile-test.rs | 100 +- src/tools/clippy/tests/dogfood.rs | 179 +- src/tools/clippy/tests/fmt.rs | 8 - src/tools/clippy/tests/test_utils/mod.rs | 13 + .../multiple_config_files/no_warn}/Cargo.toml | 2 +- .../multiple_config_files/no_warn/clippy.toml | 1 + .../multiple_config_files/no_warn/src/main.rs | 3 + .../multiple_config_files/warn/.clippy.toml | 1 + .../multiple_config_files/warn/Cargo.toml | 8 + .../multiple_config_files/warn/clippy.toml | 1 + .../multiple_config_files/warn/src/main.rs | 5 + .../warn/src/main.stderr | 2 + .../tests/ui-internal/invalid_paths.stderr | 12 +- .../min_rust_version/min_rust_version.rs | 24 +- .../min_rust_version/min_rust_version.stderr | 10 + .../toml_disallowed_methods/clippy.toml | 2 + .../conf_disallowed_methods.rs | 7 +- .../conf_disallowed_methods.stderr | 14 +- src/tools/clippy/tests/ui/as_conversions.rs | 1 + .../clippy/tests/ui/as_conversions.stderr | 6 +- .../tests/ui/assertions_on_constants.rs | 1 - .../tests/ui/assertions_on_constants.stderr | 42 +- .../clippy/tests/ui/author/struct.stdout | 2 +- src/tools/clippy/tests/ui/borrow_as_ptr.fixed | 10 + src/tools/clippy/tests/ui/borrow_as_ptr.rs | 10 + .../clippy/tests/ui/borrow_as_ptr.stderr | 16 + .../tests/ui/borrow_as_ptr_no_std.fixed | 22 + .../clippy/tests/ui/borrow_as_ptr_no_std.rs | 22 + .../tests/ui/borrow_as_ptr_no_std.stderr | 16 + .../borrow_interior_mutable_const/others.rs | 6 +- .../others.stderr | 28 +- src/tools/clippy/tests/ui/bytecount.rs | 2 + src/tools/clippy/tests/ui/bytecount.stderr | 8 +- src/tools/clippy/tests/ui/cast_alignment.rs | 8 +- .../clippy/tests/ui/cast_alignment.stderr | 8 +- src/tools/clippy/tests/ui/cast_ref_to_mut.rs | 2 +- src/tools/clippy/tests/ui/clone_on_copy.fixed | 3 +- src/tools/clippy/tests/ui/clone_on_copy.rs | 3 +- .../clippy/tests/ui/clone_on_copy.stderr | 16 +- .../tests/ui/cmp_owned/comparison_flip.fixed | 29 + .../tests/ui/cmp_owned/comparison_flip.rs | 29 + .../tests/ui/cmp_owned/comparison_flip.stderr | 18 + src/tools/clippy/tests/ui/crashes/ice-4968.rs | 1 + src/tools/clippy/tests/ui/crashes/ice-8250.rs | 6 + .../clippy/tests/ui/crashes/ice-8250.stderr | 18 + src/tools/clippy/tests/ui/crashes/ice-8386.rs | 3 + .../tests/ui/default_union_representation.rs | 78 + .../ui/default_union_representation.stderr | 48 + src/tools/clippy/tests/ui/deref_addrof.fixed | 2 + src/tools/clippy/tests/ui/deref_addrof.rs | 2 + src/tools/clippy/tests/ui/deref_addrof.stderr | 4 +- .../clippy/tests/ui/duration_subsec.fixed | 2 +- src/tools/clippy/tests/ui/duration_subsec.rs | 2 +- src/tools/clippy/tests/ui/enum_variants.rs | 7 + src/tools/clippy/tests/ui/eq_op.rs | 93 +- src/tools/clippy/tests/ui/eq_op.stderr | 162 +- src/tools/clippy/tests/ui/eq_op_macros.stderr | 44 +- src/tools/clippy/tests/ui/erasing_op.rs | 34 + src/tools/clippy/tests/ui/erasing_op.stderr | 20 +- src/tools/clippy/tests/ui/eta.fixed | 20 +- src/tools/clippy/tests/ui/eta.rs | 18 +- src/tools/clippy/tests/ui/eta.stderr | 50 +- .../clippy/tests/ui/expect_fun_call.fixed | 9 + src/tools/clippy/tests/ui/expect_fun_call.rs | 9 + .../clippy/tests/ui/expect_fun_call.stderr | 32 +- .../tests/ui/explicit_counter_loop.stderr | 4 +- .../tests/ui/explicit_deref_methods.fixed | 2 +- .../clippy/tests/ui/explicit_deref_methods.rs | 2 +- .../clippy/tests/ui/explicit_write.fixed | 12 + src/tools/clippy/tests/ui/explicit_write.rs | 12 + .../clippy/tests/ui/explicit_write.stderr | 42 +- .../tests/ui/explicit_write_non_rustfix.rs | 8 - .../ui/explicit_write_non_rustfix.stderr | 11 - .../clippy/tests/ui/for_loop_fixable.fixed | 7 +- src/tools/clippy/tests/ui/for_loop_fixable.rs | 7 +- .../clippy/tests/ui/for_loop_fixable.stderr | 30 +- src/tools/clippy/tests/ui/format.fixed | 7 +- src/tools/clippy/tests/ui/format.rs | 7 +- src/tools/clippy/tests/ui/format.stderr | 34 +- src/tools/clippy/tests/ui/functions.rs | 8 + src/tools/clippy/tests/ui/functions.stderr | 26 +- src/tools/clippy/tests/ui/get_unwrap.fixed | 12 +- src/tools/clippy/tests/ui/get_unwrap.rs | 12 +- src/tools/clippy/tests/ui/get_unwrap.stderr | 135 +- src/tools/clippy/tests/ui/identity_op.rs | 13 + src/tools/clippy/tests/ui/identity_op.stderr | 26 +- .../clippy/tests/ui/if_same_then_else2.rs | 17 + src/tools/clippy/tests/ui/implicit_clone.rs | 9 + .../clippy/tests/ui/implicit_clone.stderr | 54 +- src/tools/clippy/tests/ui/issue_4266.stderr | 11 +- .../tests/ui/iter_not_returning_iterator.rs | 27 + .../ui/iter_not_returning_iterator.stderr | 8 +- .../tests/ui/iter_overeager_cloned.fixed | 45 + .../clippy/tests/ui/iter_overeager_cloned.rs | 47 + .../tests/ui/iter_overeager_cloned.stderr | 58 + .../clippy/tests/ui/macro_use_imports.fixed | 2 +- .../clippy/tests/ui/macro_use_imports.rs | 2 +- .../tests/ui/manual_assert.edition2018.fixed | 7 + .../tests/ui/manual_assert.edition2018.stderr | 24 +- .../tests/ui/manual_assert.edition2021.fixed | 7 + .../tests/ui/manual_assert.edition2021.stderr | 24 +- src/tools/clippy/tests/ui/manual_assert.rs | 9 + src/tools/clippy/tests/ui/manual_bits.fixed | 48 + src/tools/clippy/tests/ui/manual_bits.rs | 48 + src/tools/clippy/tests/ui/manual_bits.stderr | 154 + src/tools/clippy/tests/ui/manual_flatten.rs | 2 - .../clippy/tests/ui/manual_flatten.stderr | 26 +- .../manual_memcpy/with_loop_counters.stderr | 24 +- .../ui/manual_memcpy/without_loop_counters.rs | 11 + .../without_loop_counters.stderr | 28 +- .../clippy/tests/ui/missing_panics_doc.stderr | 6 - src/tools/clippy/tests/ui/mutex_atomic.rs | 2 + src/tools/clippy/tests/ui/mutex_atomic.stderr | 14 +- .../clippy/tests/ui/needless_borrow.fixed | 27 + src/tools/clippy/tests/ui/needless_borrow.rs | 27 + .../clippy/tests/ui/needless_borrow.stderr | 58 +- .../clippy/tests/ui/needless_lifetimes.rs | 51 +- .../clippy/tests/ui/needless_lifetimes.stderr | 94 +- .../tests/ui/needless_question_mark.fixed | 13 + .../clippy/tests/ui/needless_question_mark.rs | 13 + .../tests/ui/needless_question_mark.stderr | 14 +- .../tests/ui/non_expressive_names.stdout | 0 .../tests/ui/non_send_fields_in_send_ty.rs | 1 + .../ui/non_send_fields_in_send_ty.stderr | 52 +- src/tools/clippy/tests/ui/op_ref.rs | 39 +- src/tools/clippy/tests/ui/op_ref.stderr | 18 +- src/tools/clippy/tests/ui/or_fun_call.fixed | 50 +- src/tools/clippy/tests/ui/or_fun_call.rs | 52 +- src/tools/clippy/tests/ui/or_fun_call.stderr | 54 +- .../clippy/tests/ui/panic_in_result_fn.stderr | 6 - .../ui/panic_in_result_fn_assertions.stderr | 3 - .../clippy/tests/ui/panicking_macros.stderr | 18 - src/tools/clippy/tests/ui/ptr_arg.rs | 35 +- src/tools/clippy/tests/ui/ptr_arg.stderr | 158 +- .../redundant_pattern_matching_option.fixed | 2 +- .../ui/redundant_pattern_matching_option.rs | 2 +- src/tools/clippy/tests/ui/rename.fixed | 1 + src/tools/clippy/tests/ui/rename.rs | 1 + src/tools/clippy/tests/ui/rename.stderr | 26 +- .../tests/ui/return_self_not_must_use.stderr | 5 + .../tests/ui/search_is_some_fixable_none.rs | 2 +- .../ui/search_is_some_fixable_none.stderr | 10 +- .../tests/ui/search_is_some_fixable_some.rs | 2 +- .../ui/search_is_some_fixable_some.stderr | 10 +- .../tests/ui/single_char_lifetime_names.rs | 43 + .../ui/single_char_lifetime_names.stderr | 43 + src/tools/clippy/tests/ui/single_match.rs | 78 + src/tools/clippy/tests/ui/single_match.stderr | 38 +- .../clippy/tests/ui/single_match_else.stderr | 42 +- .../tests/ui/slow_vector_initialization.rs | 2 +- .../clippy/tests/ui/starts_ends_with.fixed | 10 +- src/tools/clippy/tests/ui/starts_ends_with.rs | 10 +- .../clippy/tests/ui/starts_ends_with.stderr | 50 +- src/tools/clippy/tests/ui/swap.fixed | 33 + src/tools/clippy/tests/ui/swap.rs | 35 + src/tools/clippy/tests/ui/swap.stderr | 12 +- .../tests/ui/to_string_in_display.stderr | 11 +- .../tests/ui/trait_duplication_in_bounds.rs | 67 + .../ui/trait_duplication_in_bounds.stderr | 54 +- src/tools/clippy/tests/ui/transmute.rs | 2 +- .../clippy/tests/ui/transmute_ptr_to_ptr.rs | 1 + .../tests/ui/transmute_ptr_to_ptr.stderr | 12 +- .../tests/ui/transmute_undefined_repr.rs | 44 + .../tests/ui/transmute_undefined_repr.stderr | 44 + .../transmutes_expressible_as_ptr_casts.fixed | 3 +- .../ui/transmutes_expressible_as_ptr_casts.rs | 3 +- ...transmutes_expressible_as_ptr_casts.stderr | 16 +- .../tests/ui/type_repetition_in_bounds.rs | 10 + src/tools/clippy/tests/ui/unit_cmp.stderr | 8 - src/tools/clippy/tests/ui/unnecessary_cast.rs | 7 + .../clippy/tests/ui/unnecessary_cast.stderr | 38 +- .../tests/ui/unnecessary_cast_fixable.fixed | 7 +- .../tests/ui/unnecessary_cast_fixable.rs | 7 +- .../tests/ui/unnecessary_cast_fixable.stderr | 34 +- .../clippy/tests/ui/unnecessary_ref.fixed | 23 - src/tools/clippy/tests/ui/unnecessary_ref.rs | 23 - .../clippy/tests/ui/unnecessary_ref.stderr | 22 - src/tools/clippy/tests/ui/unused_io_amount.rs | 53 + .../clippy/tests/ui/unused_io_amount.stderr | 101 +- src/tools/clippy/tests/ui/useless_asref.fixed | 2 +- src/tools/clippy/tests/ui/useless_asref.rs | 2 +- .../tests/ui/vtable_address_comparisons.rs | 2 + .../ui/vtable_address_comparisons.stderr | 20 +- .../tests/ui/while_let_on_iterator.fixed | 30 + .../clippy/tests/ui/while_let_on_iterator.rs | 30 + .../tests/ui/while_let_on_iterator.stderr | 16 +- src/tools/clippy/tests/ui/write_literal.rs | 48 +- .../clippy/tests/ui/write_literal.stderr | 110 +- src/tools/clippy/tests/ui/write_literal_2.rs | 16 +- .../clippy/tests/ui/write_literal_2.stderr | 50 +- .../clippy/tests/ui/write_with_newline.rs | 56 +- .../clippy/tests/ui/write_with_newline.stderr | 72 +- .../tests/ui/writeln_empty_string.fixed | 10 +- .../clippy/tests/ui/writeln_empty_string.rs | 10 +- .../tests/ui/writeln_empty_string.stderr | 12 +- .../clippy/tests/ui/wrong_self_convention.rs | 21 + .../tests/ui/wrong_self_convention.stderr | 10 +- src/tools/clippy/tests/ui/zero_offset.rs | 1 + src/tools/clippy/tests/ui/zero_offset.stderr | 16 +- src/tools/clippy/tests/ui_test/eq_op.rs | 15 - src/tools/clippy/tests/workspace.rs | 107 + .../workspace_test}/Cargo.toml | 2 +- .../workspace_test}/build.rs | 0 .../workspace_test}/path_dep/Cargo.toml | 0 .../workspace_test}/path_dep/src/lib.rs | 0 .../workspace_test}/src/main.rs | 0 .../workspace_test}/subcrate/Cargo.toml | 0 .../workspace_test}/subcrate/src/lib.rs | 0 src/tools/compiletest/src/common.rs | 1 + src/tools/compiletest/src/header.rs | 401 +- src/tools/compiletest/src/header/tests.rs | 1 + src/tools/compiletest/src/main.rs | 2 + src/tools/compiletest/src/runtest.rs | 177 +- src/tools/compiletest/src/util.rs | 3 + src/tools/jsondocck/src/main.rs | 18 +- src/tools/linkchecker/main.rs | 5 +- src/tools/rust-demangler/tests/lib.rs | 32 +- src/tools/rustdoc-js/tester.js | 2 + src/tools/rustfmt/src/expr.rs | 4 +- src/tools/rustfmt/src/ignore_path.rs | 2 +- src/tools/rustfmt/src/lib.rs | 1 + src/tools/rustfmt/src/parse/session.rs | 2 +- src/tools/rustfmt/src/types.rs | 17 +- src/tools/rustfmt/src/utils.rs | 1 - src/tools/tidy/Cargo.toml | 2 +- src/tools/tidy/src/deps.rs | 31 +- src/tools/tidy/src/edition.rs | 33 +- src/tools/tidy/src/error_codes_check.rs | 33 +- src/tools/tidy/src/pal.rs | 1 + src/tools/tidy/src/ui_tests.rs | 2 +- src/version | 2 +- vendor/ahash/.cargo-checksum.json | 1 + vendor/ahash/Cargo.toml | 117 + vendor/ahash/FAQ.md | 118 + .../LICENSE-APACHE | 0 vendor/{maybe-uninit => ahash}/LICENSE-MIT | 2 +- vendor/ahash/README.md | 110 + vendor/ahash/build.rs | 44 + vendor/ahash/rustfmt.toml | 1 + vendor/ahash/src/aes_hash.rs | 439 ++ vendor/ahash/src/convert.rs | 179 + vendor/ahash/src/fallback_hash.rs | 392 + vendor/ahash/src/hash_map.rs | 371 + vendor/ahash/src/hash_quality_test.rs | 460 ++ vendor/ahash/src/hash_set.rs | 313 + vendor/ahash/src/lib.rs | 253 + vendor/ahash/src/operations.rs | 299 + vendor/ahash/src/random_state.rs | 295 + vendor/ahash/src/specialize.rs | 229 + vendor/ahash/tests/bench.rs | 142 + vendor/ahash/tests/map_tests.rs | 203 + vendor/ahash/tests/nopanic.rs | 72 + vendor/ammonia/.cargo-checksum.json | 2 +- vendor/ammonia/CHANGELOG.md | 22 + vendor/ammonia/Cargo.lock | 634 +- vendor/ammonia/Cargo.toml | 15 +- vendor/ammonia/README.md | 5 +- vendor/ammonia/SECURITY.md | 16 + vendor/ammonia/bors.toml | 2 + vendor/ammonia/src/lib.rs | 688 +- vendor/ansi_term-0.11.0/.cargo-checksum.json | 1 - vendor/ansi_term-0.11.0/Cargo.toml | 27 - vendor/ansi_term-0.11.0/LICENCE | 21 - vendor/ansi_term-0.11.0/README.md | 174 - vendor/ansi_term-0.11.0/examples/colours.rs | 13 - vendor/ansi_term-0.11.0/src/ansi.rs | 258 - vendor/ansi_term-0.11.0/src/debug.rs | 122 - vendor/ansi_term-0.11.0/src/difference.rs | 179 - vendor/ansi_term-0.11.0/src/display.rs | 279 - vendor/ansi_term-0.11.0/src/lib.rs | 205 - vendor/ansi_term-0.11.0/src/style.rs | 259 - vendor/ansi_term-0.11.0/src/windows.rs | 40 - vendor/ansi_term-0.11.0/src/write.rs | 40 - vendor/anyhow/.cargo-checksum.json | 2 +- vendor/anyhow/Cargo.toml | 4 +- vendor/anyhow/src/backtrace.rs | 4 +- vendor/anyhow/src/lib.rs | 3 +- vendor/anyhow/tests/drop/mod.rs | 7 +- vendor/anyhow/tests/test_ensure.rs | 66 +- vendor/askama/.cargo-checksum.json | 1 + vendor/askama/Cargo.toml | 61 + vendor/{parking_lot => askama}/LICENSE-APACHE | 2 +- .../LICENSE-MIT | 2 +- vendor/askama/src/lib.rs | 200 + vendor/askama_derive/.cargo-checksum.json | 1 + vendor/askama_derive/Cargo.toml | 43 + .../LICENSE-APACHE | 2 +- .../LICENSE-MIT | 50 +- vendor/askama_derive/README.md | 9 + vendor/askama_derive/src/lib.rs | 121 + vendor/askama_escape/.cargo-checksum.json | 1 + vendor/askama_escape/Cargo.toml | 30 + .../LICENSE-APACHE | 402 +- .../LICENSE-MIT | 50 +- vendor/askama_escape/README.md | 9 + vendor/askama_escape/benches/all.rs | 77 + vendor/askama_escape/src/lib.rs | 186 + vendor/askama_shared/.cargo-checksum.json | 1 + vendor/askama_shared/Cargo.toml | 75 + vendor/askama_shared/LICENSE-APACHE | 25 + vendor/askama_shared/LICENSE-MIT | 25 + vendor/askama_shared/README.md | 9 + vendor/askama_shared/src/error.rs | 90 + vendor/askama_shared/src/filters/json.rs | 37 + vendor/askama_shared/src/filters/mod.rs | 631 ++ vendor/askama_shared/src/filters/yaml.rs | 34 + vendor/askama_shared/src/generator.rs | 1874 +++++ vendor/askama_shared/src/helpers/mod.rs | 49 + vendor/askama_shared/src/heritage.rs | 125 + vendor/askama_shared/src/input.rs | 288 + vendor/askama_shared/src/lib.rs | 521 ++ vendor/askama_shared/src/parser.rs | 1627 ++++ vendor/askama_shared/templates/a.html | 1 + vendor/askama_shared/templates/b.html | 1 + vendor/askama_shared/templates/sub/b.html | 1 + vendor/askama_shared/templates/sub/c.html | 1 + .../askama_shared/templates/sub/sub1/d.html | 1 + vendor/autocfg/.cargo-checksum.json | 2 +- vendor/autocfg/Cargo.lock | 5 +- vendor/autocfg/Cargo.toml | 11 +- vendor/autocfg/README.md | 3 + vendor/autocfg/src/lib.rs | 79 +- vendor/autocfg/src/tests.rs | 13 +- vendor/autocfg/tests/rustflags.rs | 24 +- vendor/block-buffer/.cargo-checksum.json | 2 +- vendor/block-buffer/CHANGELOG.md | 41 + vendor/block-buffer/Cargo.toml | 18 +- vendor/block-buffer/README.md | 40 + vendor/block-buffer/src/lib.rs | 456 +- vendor/block-buffer/src/sealed.rs | 67 + vendor/block-buffer/tests/mod.rs | 188 + vendor/bytes/.cargo-checksum.json | 1 + vendor/bytes/CHANGELOG.md | 199 + .../Cargo.toml | 37 +- vendor/bytes/LICENSE | 25 + vendor/bytes/README.md | 47 + vendor/bytes/benches/buf.rs | 186 + vendor/bytes/benches/bytes.rs | 119 + vendor/bytes/benches/bytes_mut.rs | 266 + vendor/{memoffset-0.5.5 => bytes}/ci/miri.sh | 11 +- vendor/bytes/ci/test-stable.sh | 27 + vendor/bytes/ci/tsan.sh | 13 + vendor/bytes/src/buf/buf_impl.rs | 1071 +++ vendor/bytes/src/buf/buf_mut.rs | 1079 +++ vendor/bytes/src/buf/chain.rs | 222 + vendor/bytes/src/buf/iter.rs | 132 + vendor/bytes/src/buf/limit.rs | 75 + vendor/bytes/src/buf/mod.rs | 41 + vendor/bytes/src/buf/reader.rs | 81 + vendor/bytes/src/buf/take.rs | 147 + vendor/bytes/src/buf/uninit_slice.rs | 176 + vendor/bytes/src/buf/vec_deque.rs | 22 + vendor/bytes/src/buf/writer.rs | 88 + vendor/bytes/src/bytes.rs | 1138 +++ vendor/bytes/src/bytes_mut.rs | 1581 ++++ vendor/bytes/src/fmt/debug.rs | 49 + vendor/bytes/src/fmt/hex.rs | 37 + vendor/bytes/src/fmt/mod.rs | 5 + vendor/bytes/src/lib.rs | 117 + vendor/bytes/src/loom.rs | 30 + vendor/bytes/src/serde.rs | 89 + vendor/bytes/tests/test_buf.rs | 120 + vendor/bytes/tests/test_buf_mut.rs | 129 + vendor/bytes/tests/test_bytes.rs | 963 +++ vendor/bytes/tests/test_bytes_odd_alloc.rs | 69 + vendor/bytes/tests/test_bytes_vec_alloc.rs | 79 + vendor/bytes/tests/test_chain.rs | 134 + vendor/bytes/tests/test_debug.rs | 35 + vendor/bytes/tests/test_iter.rs | 21 + vendor/bytes/tests/test_reader.rs | 29 + vendor/bytes/tests/test_serde.rs | 20 + vendor/bytes/tests/test_take.rs | 12 + vendor/camino/.cargo-checksum.json | 2 +- vendor/camino/CHANGELOG.md | 36 +- vendor/camino/Cargo.lock | 277 - vendor/camino/Cargo.toml | 25 +- vendor/camino/README.md | 13 +- vendor/camino/examples/serde.rs | 64 - vendor/camino/examples/structopt.rs | 33 - vendor/camino/src/lib.rs | 37 +- vendor/camino/src/tests.rs | 2 +- .../.cargo-checksum.json | 1 - vendor/cargo_metadata-0.12.0/README.md | 12 - .../cargo_metadata-0.12.0/src/dependency.rs | 86 - .../cargo_metadata-0.12.0/src/diagnostic.rs | 159 - vendor/cargo_metadata-0.12.0/src/errors.rs | 106 - vendor/cargo_metadata-0.12.0/src/lib.rs | 599 -- vendor/cargo_metadata-0.12.0/src/messages.rs | 165 - .../cargo_metadata-0.12.0/tests/selftest.rs | 159 - .../tests/test_samples.rs | 583 -- vendor/chalk-derive/.cargo-checksum.json | 2 +- vendor/chalk-derive/Cargo.toml | 2 +- vendor/chalk-engine/.cargo-checksum.json | 2 +- vendor/chalk-engine/Cargo.toml | 8 +- vendor/chalk-ir/.cargo-checksum.json | 2 +- vendor/chalk-ir/Cargo.toml | 4 +- vendor/chalk-ir/src/interner.rs | 6 +- vendor/chalk-solve/.cargo-checksum.json | 2 +- vendor/chalk-solve/Cargo.toml | 9 +- vendor/chalk-solve/src/coherence.rs | 27 +- vendor/chalk-solve/src/display/state.rs | 14 +- vendor/chalk-solve/src/infer/canonicalize.rs | 6 - vendor/chalk-solve/src/logging_db.rs | 43 +- .../src/logging_db/id_collector.rs | 11 +- .../.cargo-checksum.json | 0 vendor/{clap => clap-2.34.0}/CHANGELOG.md | 0 vendor/{clap => clap-2.34.0}/CONTRIBUTORS.md | 0 vendor/{clap => clap-2.34.0}/Cargo.toml | 0 vendor/{clap => clap-2.34.0}/LICENSE-MIT | 0 vendor/{clap => clap-2.34.0}/README.md | 0 vendor/{clap => clap-2.34.0}/SPONSORS.md | 0 vendor/{clap => clap-2.34.0}/clap-test.rs | 0 vendor/{clap => clap-2.34.0}/justfile | 0 vendor/{clap => clap-2.34.0}/src/app/help.rs | 0 vendor/{clap => clap-2.34.0}/src/app/meta.rs | 0 vendor/{clap => clap-2.34.0}/src/app/mod.rs | 0 .../{clap => clap-2.34.0}/src/app/parser.rs | 0 .../{clap => clap-2.34.0}/src/app/settings.rs | 0 vendor/{clap => clap-2.34.0}/src/app/usage.rs | 0 .../src/app/validator.rs | 0 .../{clap => clap-2.34.0}/src/args/any_arg.rs | 0 vendor/{clap => clap-2.34.0}/src/args/arg.rs | 0 .../src/args/arg_builder/base.rs | 0 .../src/args/arg_builder/flag.rs | 0 .../src/args/arg_builder/mod.rs | 0 .../src/args/arg_builder/option.rs | 0 .../src/args/arg_builder/positional.rs | 0 .../src/args/arg_builder/switched.rs | 0 .../src/args/arg_builder/valued.rs | 0 .../src/args/arg_matcher.rs | 0 .../src/args/arg_matches.rs | 0 .../{clap => clap-2.34.0}/src/args/group.rs | 0 .../{clap => clap-2.34.0}/src/args/macros.rs | 0 .../src/args/matched_arg.rs | 0 vendor/{clap => clap-2.34.0}/src/args/mod.rs | 0 .../src/args/settings.rs | 0 .../src/args/subcommand.rs | 0 .../src/completions/bash.rs | 0 .../src/completions/elvish.rs | 0 .../src/completions/fish.rs | 0 .../src/completions/macros.rs | 0 .../src/completions/mod.rs | 0 .../src/completions/powershell.rs | 0 .../src/completions/shell.rs | 0 .../src/completions/zsh.rs | 0 vendor/{clap => clap-2.34.0}/src/errors.rs | 0 vendor/{clap => clap-2.34.0}/src/fmt.rs | 0 vendor/{clap => clap-2.34.0}/src/lib.rs | 0 vendor/{clap => clap-2.34.0}/src/macros.rs | 0 vendor/{clap => clap-2.34.0}/src/map.rs | 0 .../{clap => clap-2.34.0}/src/osstringext.rs | 0 vendor/{clap => clap-2.34.0}/src/strext.rs | 0 .../{clap => clap-2.34.0}/src/suggestions.rs | 0 .../{clap => clap-2.34.0}/src/usage_parser.rs | 0 vendor/compiler_builtins/.cargo-checksum.json | 2 +- vendor/compiler_builtins/Cargo.lock | 6 +- vendor/compiler_builtins/Cargo.toml | 26 +- .../compiler_builtins/libm/src/math/ceil.rs | 19 + .../compiler_builtins/libm/src/math/ceilf.rs | 2 + .../compiler_builtins/libm/src/math/fabsf.rs | 2 + .../compiler_builtins/libm/src/math/floor.rs | 19 + .../compiler_builtins/libm/src/math/floorf.rs | 2 + vendor/compiler_builtins/libm/src/math/fma.rs | 6 +- vendor/compiler_builtins/libm/src/math/j1f.rs | 10 +- vendor/compiler_builtins/libm/src/math/mod.rs | 4 +- vendor/compiler_builtins/libm/src/math/pow.rs | 4 + .../libm/src/math/rem_pio2.rs | 23 +- .../libm/src/math/rem_pio2f.rs | 7 +- .../compiler_builtins/libm/src/math/round.rs | 46 +- .../compiler_builtins/libm/src/math/roundf.rs | 46 +- vendor/compiler_builtins/libm/src/math/sin.rs | 5 +- .../compiler_builtins/libm/src/math/sincos.rs | 74 + .../libm/src/math/sincosf.rs | 38 +- .../compiler_builtins/libm/src/math/sqrtf.rs | 2 + .../compiler_builtins/libm/src/math/truncf.rs | 2 + vendor/compiler_builtins/src/arm.rs | 376 +- vendor/compiler_builtins/src/arm_linux.rs | 21 +- vendor/compiler_builtins/src/float/conv.rs | 116 +- vendor/compiler_builtins/src/macros.rs | 119 +- vendor/compiler_builtins/src/mem/mod.rs | 112 +- vendor/compiler_builtins/src/x86.rs | 148 +- vendor/compiler_builtins/src/x86_64.rs | 158 +- vendor/cpufeatures/.cargo-checksum.json | 1 + vendor/cpufeatures/CHANGELOG.md | 60 + vendor/{cpuid-bool => cpufeatures}/Cargo.toml | 13 +- .../LICENSE-APACHE | 0 .../{cpuid-bool => cpufeatures}/LICENSE-MIT | 0 vendor/cpufeatures/README.md | 86 + vendor/cpufeatures/src/aarch64.rs | 178 + vendor/cpufeatures/src/lib.rs | 136 + vendor/cpufeatures/src/x86.rs | 81 + vendor/cpufeatures/tests/aarch64.rs | 17 + vendor/cpufeatures/tests/x86.rs | 17 + vendor/cpuid-bool/.cargo-checksum.json | 1 - vendor/cpuid-bool/CHANGELOG.md | 21 - vendor/cpuid-bool/src/lib.rs | 121 - vendor/crc32fast/.cargo-checksum.json | 2 +- vendor/crc32fast/Cargo.toml | 2 +- vendor/crc32fast/src/specialized/aarch64.rs | 2 +- vendor/crc32fast/src/table.rs | 5 +- vendor/crossbeam-channel/.cargo-checksum.json | 2 +- vendor/crossbeam-channel/CHANGELOG.md | 4 + vendor/crossbeam-channel/Cargo.lock | 57 +- vendor/crossbeam-channel/Cargo.toml | 14 +- vendor/crossbeam-channel/benches/crossbeam.rs | 2 +- .../crossbeam-channel/examples/fibonacci.rs | 2 +- .../crossbeam-channel/examples/stopwatch.rs | 6 +- vendor/crossbeam-channel/src/context.rs | 20 +- vendor/crossbeam-channel/src/flavors/array.rs | 8 +- vendor/crossbeam-channel/src/flavors/zero.rs | 63 +- vendor/crossbeam-channel/src/select.rs | 5 + vendor/crossbeam-channel/src/waker.rs | 51 +- vendor/crossbeam-channel/tests/after.rs | 16 +- vendor/crossbeam-channel/tests/array.rs | 34 +- vendor/crossbeam-channel/tests/golang.rs | 67 +- vendor/crossbeam-channel/tests/iter.rs | 4 +- vendor/crossbeam-channel/tests/list.rs | 53 +- vendor/crossbeam-channel/tests/mpsc.rs | 82 +- vendor/crossbeam-channel/tests/never.rs | 4 +- vendor/crossbeam-channel/tests/ready.rs | 20 + vendor/crossbeam-channel/tests/select.rs | 62 +- .../crossbeam-channel/tests/select_macro.rs | 34 + .../crossbeam-channel/tests/thread_locals.rs | 2 + vendor/crossbeam-channel/tests/tick.rs | 14 +- vendor/crossbeam-channel/tests/zero.rs | 43 +- .../.cargo-checksum.json | 1 - vendor/crossbeam-deque-0.7.4/CHANGELOG.md | 99 - vendor/crossbeam-deque-0.7.4/Cargo.toml | 33 - vendor/crossbeam-deque-0.7.4/LICENSE-MIT | 27 - vendor/crossbeam-deque-0.7.4/README.md | 50 - vendor/crossbeam-deque-0.7.4/src/lib.rs | 2036 ----- vendor/crossbeam-deque-0.7.4/tests/fifo.rs | 342 - .../crossbeam-deque-0.7.4/tests/injector.rs | 353 - vendor/crossbeam-deque-0.7.4/tests/lifo.rs | 342 - vendor/crossbeam-deque-0.7.4/tests/steal.rs | 214 - .../.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 - 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 - vendor/crossbeam-epoch-0.8.2/build.rs | 8 - .../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 | 16 +- vendor/crossbeam-epoch/Cargo.lock | 237 +- vendor/crossbeam-epoch/Cargo.toml | 46 +- vendor/crossbeam-epoch/benches/pin.rs | 2 +- vendor/crossbeam-epoch/build.rs | 15 +- vendor/crossbeam-epoch/no_atomic.rs | 22 +- vendor/crossbeam-epoch/src/atomic.rs | 67 +- vendor/crossbeam-epoch/src/collector.rs | 30 +- vendor/crossbeam-epoch/src/deferred.rs | 11 +- vendor/crossbeam-epoch/src/internal.rs | 6 +- vendor/crossbeam-epoch/src/sync/queue.rs | 7 +- vendor/crossbeam-queue/.cargo-checksum.json | 1 - vendor/crossbeam-queue/CHANGELOG.md | 28 - vendor/crossbeam-queue/Cargo.toml | 40 - vendor/crossbeam-queue/LICENSE-MIT | 27 - vendor/crossbeam-queue/LICENSE-THIRD-PARTY | 31 - vendor/crossbeam-queue/README.md | 67 - vendor/crossbeam-queue/src/array_queue.rs | 440 -- vendor/crossbeam-queue/src/err.rs | 47 - vendor/crossbeam-queue/src/lib.rs | 42 - vendor/crossbeam-queue/src/seg_queue.rs | 490 -- vendor/crossbeam-queue/tests/array_queue.rs | 254 - vendor/crossbeam-queue/tests/seg_queue.rs | 167 - .../.cargo-checksum.json | 1 - vendor/crossbeam-utils-0.7.2/CHANGELOG.md | 113 - vendor/crossbeam-utils-0.7.2/Cargo.toml | 40 - vendor/crossbeam-utils-0.7.2/LICENSE-MIT | 27 - vendor/crossbeam-utils-0.7.2/README.md | 77 - .../benches/atomic_cell.rs | 157 - vendor/crossbeam-utils-0.7.2/build.rs | 14 - .../src/atomic/atomic_cell.rs | 890 --- .../src/atomic/consume.rs | 82 - .../crossbeam-utils-0.7.2/src/atomic/mod.rs | 25 - .../src/atomic/seq_lock.rs | 88 - .../src/atomic/seq_lock_wide.rs | 134 - vendor/crossbeam-utils-0.7.2/src/backoff.rs | 292 - .../crossbeam-utils-0.7.2/src/cache_padded.rs | 131 - vendor/crossbeam-utils-0.7.2/src/lib.rs | 64 - vendor/crossbeam-utils-0.7.2/src/sync/mod.rs | 17 - .../crossbeam-utils-0.7.2/src/sync/parker.rs | 315 - .../src/sync/sharded_lock.rs | 608 -- .../src/sync/wait_group.rs | 137 - vendor/crossbeam-utils-0.7.2/src/thread.rs | 529 -- .../tests/atomic_cell.rs | 233 - .../tests/cache_padded.rs | 112 - vendor/crossbeam-utils-0.7.2/tests/parker.rs | 43 - .../tests/sharded_lock.rs | 255 - vendor/crossbeam-utils-0.7.2/tests/thread.rs | 181 - .../crossbeam-utils-0.7.2/tests/wait_group.rs | 66 - vendor/crossbeam-utils/.cargo-checksum.json | 2 +- vendor/crossbeam-utils/CHANGELOG.md | 15 +- vendor/crossbeam-utils/Cargo.toml | 34 +- vendor/crossbeam-utils/build.rs | 29 +- vendor/crossbeam-utils/no_atomic.rs | 22 +- .../crossbeam-utils/src/atomic/atomic_cell.rs | 489 +- vendor/crossbeam-utils/src/sync/parker.rs | 2 + vendor/crossbeam-utils/src/thread.rs | 2 +- vendor/crossbeam-utils/tests/atomic_cell.rs | 66 + vendor/crossbeam-utils/tests/sharded_lock.rs | 3 + vendor/crypto-common/.cargo-checksum.json | 1 + vendor/crypto-common/CHANGELOG.md | 23 + vendor/crypto-common/Cargo.toml | 36 + .../LICENSE-APACHE | 0 vendor/crypto-common/LICENSE-MIT | 25 + vendor/crypto-common/README.md | 53 + vendor/crypto-common/src/lib.rs | 300 + vendor/difference/.cargo-checksum.json | 1 - vendor/difference/Cargo.toml | 44 - vendor/difference/Examples.md | 21 - vendor/difference/LICENSE | 22 - vendor/difference/README.md | 46 - vendor/difference/appveyor.yml | 43 - vendor/difference/assets/fox.png | Bin 56879 -> 0 bytes vendor/difference/assets/git-style.png | Bin 34059 -> 0 bytes vendor/difference/assets/github-style.png | Bin 38391 -> 0 bytes vendor/difference/assets/word-underline.png | Bin 15775 -> 0 bytes vendor/difference/examples/github-style.rs | 70 - vendor/difference/examples/line-by-line.rs | 52 - vendor/difference/examples/underline-words.rs | 54 - vendor/difference/rustfmt.toml | 3 - vendor/difference/src/display.rs | 100 - vendor/difference/src/lcs.rs | 131 - vendor/difference/src/lib.rs | 334 - vendor/difference/src/main.rs | 46 - vendor/difference/src/merge.rs | 92 - vendor/difference/tests/quickcheck.rs | 132 - vendor/digest/.cargo-checksum.json | 2 +- vendor/digest/CHANGELOG.md | 28 + vendor/digest/Cargo.toml | 32 +- vendor/digest/README.md | 3 + vendor/digest/src/core_api.rs | 119 + vendor/digest/src/core_api/ct_variable.rs | 167 + vendor/digest/src/core_api/rt_variable.rs | 166 + vendor/digest/src/core_api/wrapper.rs | 277 + vendor/digest/src/core_api/xof_reader.rs | 63 + vendor/digest/src/dev.rs | 261 +- vendor/digest/src/dev/fixed.rs | 65 + vendor/digest/src/dev/mac.rs | 159 + vendor/digest/src/dev/rng.rs | 38 + vendor/digest/src/dev/variable.rs | 82 + vendor/digest/src/dev/xof.rs | 51 + vendor/digest/src/digest.rs | 246 +- vendor/digest/src/dyn_digest.rs | 64 - vendor/digest/src/errors.rs | 14 - vendor/digest/src/fixed.rs | 71 - vendor/digest/src/lib.rs | 325 +- vendor/digest/src/mac.rs | 261 + vendor/digest/src/variable.rs | 106 - vendor/digest/src/xof.rs | 102 - vendor/futures-channel/.cargo-checksum.json | 1 + vendor/futures-channel/Cargo.toml | 41 + .../LICENSE-APACHE | 403 +- vendor/futures-channel/LICENSE-MIT | 26 + vendor/futures-channel/README.md | 23 + vendor/futures-channel/benches/sync_mpsc.rs | 135 + vendor/futures-channel/build.rs | 42 + vendor/futures-channel/no_atomic_cas.rs | 13 + vendor/futures-channel/src/lib.rs | 42 + vendor/futures-channel/src/lock.rs | 102 + vendor/futures-channel/src/mpsc/mod.rs | 1308 ++++ vendor/futures-channel/src/mpsc/queue.rs | 176 + vendor/futures-channel/src/mpsc/sink_impl.rs | 73 + vendor/futures-channel/src/oneshot.rs | 488 ++ vendor/futures-channel/tests/channel.rs | 66 + vendor/futures-channel/tests/mpsc-close.rs | 298 + vendor/futures-channel/tests/mpsc.rs | 630 ++ vendor/futures-channel/tests/oneshot.rs | 252 + vendor/futures-core/.cargo-checksum.json | 1 + vendor/futures-core/Cargo.toml | 34 + vendor/futures-core/LICENSE-APACHE | 202 + vendor/futures-core/LICENSE-MIT | 26 + vendor/futures-core/README.md | 23 + vendor/futures-core/build.rs | 42 + vendor/futures-core/no_atomic_cas.rs | 13 + vendor/futures-core/src/future.rs | 103 + vendor/futures-core/src/lib.rs | 27 + vendor/futures-core/src/stream.rs | 235 + .../src/task/__internal/atomic_waker.rs | 409 + .../futures-core/src/task/__internal/mod.rs | 4 + vendor/futures-core/src/task/mod.rs | 10 + vendor/futures-core/src/task/poll.rs | 12 + vendor/futures-executor/.cargo-checksum.json | 1 + vendor/futures-executor/Cargo.toml | 45 + vendor/futures-executor/LICENSE-APACHE | 202 + vendor/futures-executor/LICENSE-MIT | 26 + vendor/futures-executor/README.md | 23 + .../futures-executor/benches/thread_notify.rs | 109 + vendor/futures-executor/src/enter.rs | 80 + vendor/futures-executor/src/lib.rs | 76 + vendor/futures-executor/src/local_pool.rs | 400 + vendor/futures-executor/src/thread_pool.rs | 375 + vendor/futures-executor/src/unpark_mutex.rs | 137 + vendor/futures-executor/tests/local_pool.rs | 434 ++ vendor/futures-io/.cargo-checksum.json | 1 + vendor/futures-io/Cargo.toml | 30 + vendor/futures-io/LICENSE-APACHE | 202 + vendor/futures-io/LICENSE-MIT | 26 + vendor/futures-io/README.md | 23 + vendor/futures-io/src/lib.rs | 558 ++ vendor/futures-macro/.cargo-checksum.json | 1 + vendor/futures-macro/Cargo.toml | 34 + vendor/futures-macro/LICENSE-APACHE | 202 + vendor/futures-macro/LICENSE-MIT | 26 + vendor/futures-macro/src/executor.rs | 55 + vendor/futures-macro/src/join.rs | 143 + vendor/futures-macro/src/lib.rs | 61 + vendor/futures-macro/src/select.rs | 330 + vendor/futures-macro/src/stream_select.rs | 113 + vendor/futures-sink/.cargo-checksum.json | 1 + vendor/futures-sink/Cargo.toml | 29 + vendor/futures-sink/LICENSE-APACHE | 202 + vendor/futures-sink/LICENSE-MIT | 26 + vendor/futures-sink/README.md | 23 + vendor/futures-sink/src/lib.rs | 240 + vendor/futures-task/.cargo-checksum.json | 1 + vendor/futures-task/Cargo.toml | 33 + vendor/futures-task/LICENSE-APACHE | 202 + vendor/futures-task/LICENSE-MIT | 26 + vendor/futures-task/README.md | 23 + vendor/futures-task/build.rs | 42 + vendor/futures-task/no_atomic_cas.rs | 13 + vendor/futures-task/src/arc_wake.rs | 49 + vendor/futures-task/src/future_obj.rs | 337 + vendor/futures-task/src/lib.rs | 50 + vendor/futures-task/src/noop_waker.rs | 63 + vendor/futures-task/src/spawn.rs | 192 + vendor/futures-task/src/waker.rs | 59 + vendor/futures-task/src/waker_ref.rs | 63 + vendor/futures-util/.cargo-checksum.json | 1 + vendor/futures-util/Cargo.toml | 93 + vendor/futures-util/LICENSE-APACHE | 202 + vendor/futures-util/LICENSE-MIT | 26 + vendor/futures-util/README.md | 23 + .../futures-util/benches/futures_unordered.rs | 43 + .../futures-util/benches_disabled/bilock.rs | 122 + vendor/futures-util/build.rs | 42 + vendor/futures-util/no_atomic_cas.rs | 13 + vendor/futures-util/src/abortable.rs | 185 + .../futures-util/src/async_await/join_mod.rs | 110 + vendor/futures-util/src/async_await/mod.rs | 58 + .../futures-util/src/async_await/pending.rs | 43 + vendor/futures-util/src/async_await/poll.rs | 39 + vendor/futures-util/src/async_await/random.rs | 54 + .../src/async_await/select_mod.rs | 336 + .../src/async_await/stream_select_mod.rs | 40 + .../futures-util/src/compat/compat01as03.rs | 449 ++ .../futures-util/src/compat/compat03as01.rs | 265 + vendor/futures-util/src/compat/executor.rs | 85 + vendor/futures-util/src/compat/mod.rs | 22 + vendor/futures-util/src/fns.rs | 372 + vendor/futures-util/src/future/abortable.rs | 19 + vendor/futures-util/src/future/either.rs | 297 + .../src/future/future/catch_unwind.rs | 38 + .../futures-util/src/future/future/flatten.rs | 153 + vendor/futures-util/src/future/future/fuse.rs | 93 + vendor/futures-util/src/future/future/map.rs | 66 + vendor/futures-util/src/future/future/mod.rs | 610 ++ .../src/future/future/remote_handle.rs | 126 + .../futures-util/src/future/future/shared.rs | 371 + vendor/futures-util/src/future/join.rs | 217 + vendor/futures-util/src/future/join_all.rs | 167 + vendor/futures-util/src/future/lazy.rs | 60 + vendor/futures-util/src/future/maybe_done.rs | 104 + vendor/futures-util/src/future/mod.rs | 131 + vendor/futures-util/src/future/option.rs | 64 + vendor/futures-util/src/future/pending.rs | 54 + vendor/futures-util/src/future/poll_fn.rs | 58 + .../futures-util/src/future/poll_immediate.rs | 126 + vendor/futures-util/src/future/ready.rs | 82 + vendor/futures-util/src/future/select.rs | 124 + vendor/futures-util/src/future/select_all.rs | 74 + vendor/futures-util/src/future/select_ok.rs | 85 + .../src/future/try_future/into_future.rs | 36 + .../futures-util/src/future/try_future/mod.rs | 619 ++ .../src/future/try_future/try_flatten.rs | 162 + .../src/future/try_future/try_flatten_err.rs | 62 + vendor/futures-util/src/future/try_join.rs | 256 + .../futures-util/src/future/try_join_all.rs | 137 + .../futures-util/src/future/try_maybe_done.rs | 92 + vendor/futures-util/src/future/try_select.rs | 84 + vendor/futures-util/src/io/allow_std.rs | 200 + vendor/futures-util/src/io/buf_reader.rs | 263 + vendor/futures-util/src/io/buf_writer.rs | 224 + vendor/futures-util/src/io/chain.rs | 142 + vendor/futures-util/src/io/close.rs | 28 + vendor/futures-util/src/io/copy.rs | 58 + vendor/futures-util/src/io/copy_buf.rs | 78 + vendor/futures-util/src/io/cursor.rs | 240 + vendor/futures-util/src/io/empty.rs | 59 + vendor/futures-util/src/io/fill_buf.rs | 51 + vendor/futures-util/src/io/flush.rs | 31 + vendor/futures-util/src/io/into_sink.rs | 82 + vendor/futures-util/src/io/line_writer.rs | 155 + vendor/futures-util/src/io/lines.rs | 47 + vendor/futures-util/src/io/mod.rs | 838 +++ vendor/futures-util/src/io/read.rs | 30 + vendor/futures-util/src/io/read_exact.rs | 42 + vendor/futures-util/src/io/read_line.rs | 57 + vendor/futures-util/src/io/read_to_end.rs | 91 + vendor/futures-util/src/io/read_to_string.rs | 59 + vendor/futures-util/src/io/read_until.rs | 60 + vendor/futures-util/src/io/read_vectored.rs | 30 + vendor/futures-util/src/io/repeat.rs | 66 + vendor/futures-util/src/io/seek.rs | 30 + vendor/futures-util/src/io/sink.rs | 67 + vendor/futures-util/src/io/split.rs | 115 + vendor/futures-util/src/io/take.rs | 125 + vendor/futures-util/src/io/window.rs | 104 + vendor/futures-util/src/io/write.rs | 30 + vendor/futures-util/src/io/write_all.rs | 43 + .../futures-util/src/io/write_all_vectored.rs | 193 + vendor/futures-util/src/io/write_vectored.rs | 30 + vendor/futures-util/src/lib.rs | 337 + vendor/futures-util/src/lock/bilock.rs | 276 + vendor/futures-util/src/lock/mod.rs | 25 + vendor/futures-util/src/lock/mutex.rs | 406 + vendor/futures-util/src/never.rs | 18 + vendor/futures-util/src/sink/buffer.rs | 105 + vendor/futures-util/src/sink/close.rs | 32 + vendor/futures-util/src/sink/drain.rs | 53 + vendor/futures-util/src/sink/err_into.rs | 57 + vendor/futures-util/src/sink/fanout.rs | 111 + vendor/futures-util/src/sink/feed.rs | 43 + vendor/futures-util/src/sink/flush.rs | 36 + vendor/futures-util/src/sink/map_err.rs | 65 + vendor/futures-util/src/sink/mod.rs | 344 + vendor/futures-util/src/sink/send.rs | 41 + vendor/futures-util/src/sink/send_all.rs | 100 + vendor/futures-util/src/sink/unfold.rs | 86 + vendor/futures-util/src/sink/with.rs | 134 + vendor/futures-util/src/sink/with_flat_map.rs | 127 + vendor/futures-util/src/stream/abortable.rs | 19 + vendor/futures-util/src/stream/empty.rs | 45 + .../src/stream/futures_ordered.rs | 220 + .../src/stream/futures_unordered/abort.rs | 12 + .../src/stream/futures_unordered/iter.rs | 168 + .../src/stream/futures_unordered/mod.rs | 674 ++ .../futures_unordered/ready_to_run_queue.rs | 122 + .../src/stream/futures_unordered/task.rs | 118 + vendor/futures-util/src/stream/iter.rs | 49 + vendor/futures-util/src/stream/mod.rs | 143 + vendor/futures-util/src/stream/once.rs | 67 + vendor/futures-util/src/stream/pending.rs | 45 + vendor/futures-util/src/stream/poll_fn.rs | 57 + .../futures-util/src/stream/poll_immediate.rs | 80 + vendor/futures-util/src/stream/repeat.rs | 58 + vendor/futures-util/src/stream/repeat_with.rs | 93 + vendor/futures-util/src/stream/select.rs | 117 + vendor/futures-util/src/stream/select_all.rs | 254 + .../src/stream/select_with_strategy.rs | 229 + vendor/futures-util/src/stream/stream/all.rs | 92 + vendor/futures-util/src/stream/stream/any.rs | 92 + .../src/stream/stream/buffer_unordered.rs | 124 + .../src/stream/stream/buffered.rs | 108 + .../src/stream/stream/catch_unwind.rs | 61 + .../futures-util/src/stream/stream/chain.rs | 75 + .../futures-util/src/stream/stream/chunks.rs | 106 + .../futures-util/src/stream/stream/collect.rs | 56 + .../futures-util/src/stream/stream/concat.rs | 62 + .../futures-util/src/stream/stream/count.rs | 53 + .../futures-util/src/stream/stream/cycle.rs | 68 + .../src/stream/stream/enumerate.rs | 64 + .../futures-util/src/stream/stream/filter.rs | 117 + .../src/stream/stream/filter_map.rs | 111 + .../futures-util/src/stream/stream/flatten.rs | 73 + vendor/futures-util/src/stream/stream/fold.rs | 88 + .../src/stream/stream/for_each.rs | 78 + .../src/stream/stream/for_each_concurrent.rs | 119 + .../futures-util/src/stream/stream/forward.rs | 75 + vendor/futures-util/src/stream/stream/fuse.rs | 75 + .../src/stream/stream/into_future.rs | 90 + vendor/futures-util/src/stream/stream/map.rs | 77 + vendor/futures-util/src/stream/stream/mod.rs | 1567 ++++ vendor/futures-util/src/stream/stream/next.rs | 34 + vendor/futures-util/src/stream/stream/peek.rs | 433 ++ .../src/stream/stream/ready_chunks.rs | 114 + vendor/futures-util/src/stream/stream/scan.rs | 128 + .../src/stream/stream/select_next_some.rs | 42 + vendor/futures-util/src/stream/stream/skip.rs | 70 + .../src/stream/stream/skip_while.rs | 124 + .../futures-util/src/stream/stream/split.rs | 144 + vendor/futures-util/src/stream/stream/take.rs | 86 + .../src/stream/stream/take_until.rs | 170 + .../src/stream/stream/take_while.rs | 124 + vendor/futures-util/src/stream/stream/then.rs | 101 + .../futures-util/src/stream/stream/unzip.rs | 63 + vendor/futures-util/src/stream/stream/zip.rs | 128 + .../src/stream/try_stream/and_then.rs | 105 + .../src/stream/try_stream/into_async_read.rs | 165 + .../src/stream/try_stream/into_stream.rs | 52 + .../futures-util/src/stream/try_stream/mod.rs | 1064 +++ .../src/stream/try_stream/or_else.rs | 109 + .../stream/try_stream/try_buffer_unordered.rs | 86 + .../src/stream/try_stream/try_buffered.rs | 87 + .../src/stream/try_stream/try_chunks.rs | 131 + .../src/stream/try_stream/try_collect.rs | 52 + .../src/stream/try_stream/try_concat.rs | 51 + .../src/stream/try_stream/try_filter.rs | 112 + .../src/stream/try_stream/try_filter_map.rs | 106 + .../src/stream/try_stream/try_flatten.rs | 84 + .../src/stream/try_stream/try_fold.rs | 93 + .../src/stream/try_stream/try_for_each.rs | 68 + .../try_stream/try_for_each_concurrent.rs | 133 + .../src/stream/try_stream/try_next.rs | 34 + .../src/stream/try_stream/try_skip_while.rs | 120 + .../src/stream/try_stream/try_take_while.rs | 129 + .../src/stream/try_stream/try_unfold.rs | 122 + vendor/futures-util/src/stream/unfold.rs | 119 + vendor/futures-util/src/task/mod.rs | 37 + vendor/futures-util/src/task/spawn.rs | 163 + vendor/futures-util/src/unfold_state.rs | 39 + vendor/futures/.cargo-checksum.json | 1 + vendor/futures/Cargo.toml | 87 + vendor/futures/LICENSE-APACHE | 202 + vendor/futures/LICENSE-MIT | 26 + vendor/futures/src/lib.rs | 194 + vendor/futures/tests/_require_features.rs | 13 + vendor/futures/tests/async_await_macros.rs | 389 + vendor/futures/tests/auto_traits.rs | 1891 +++++ vendor/futures/tests/compat.rs | 15 + vendor/futures/tests/eager_drop.rs | 121 + vendor/futures/tests/eventual.rs | 159 + vendor/futures/tests/future_abortable.rs | 44 + .../futures/tests/future_basic_combinators.rs | 104 + vendor/futures/tests/future_fuse.rs | 12 + vendor/futures/tests/future_inspect.rs | 16 + vendor/futures/tests/future_join_all.rs | 42 + vendor/futures/tests/future_obj.rs | 33 + vendor/futures/tests/future_select_all.rs | 25 + vendor/futures/tests/future_select_ok.rs | 30 + vendor/futures/tests/future_shared.rs | 195 + .../tests/future_try_flatten_stream.rs | 83 + vendor/futures/tests/future_try_join_all.rs | 44 + vendor/futures/tests/io_buf_reader.rs | 432 ++ vendor/futures/tests/io_buf_writer.rs | 239 + vendor/futures/tests/io_cursor.rs | 30 + vendor/futures/tests/io_line_writer.rs | 73 + vendor/futures/tests/io_lines.rs | 60 + vendor/futures/tests/io_read.rs | 64 + vendor/futures/tests/io_read_exact.rs | 17 + vendor/futures/tests/io_read_line.rs | 58 + vendor/futures/tests/io_read_to_end.rs | 65 + vendor/futures/tests/io_read_to_string.rs | 44 + vendor/futures/tests/io_read_until.rs | 60 + vendor/futures/tests/io_window.rs | 30 + vendor/futures/tests/io_write.rs | 65 + vendor/futures/tests/lock_mutex.rs | 66 + vendor/futures/tests/macro_comma_support.rs | 43 + vendor/futures/tests/object_safety.rs | 49 + vendor/futures/tests/oneshot.rs | 78 + vendor/futures/tests/ready_queue.rs | 148 + vendor/futures/tests/recurse.rs | 25 + vendor/futures/tests/sink.rs | 554 ++ vendor/futures/tests/sink_fanout.rs | 24 + vendor/futures/tests/stream.rs | 151 + vendor/futures/tests/stream_abortable.rs | 46 + .../futures/tests/stream_buffer_unordered.rs | 73 + vendor/futures/tests/stream_catch_unwind.rs | 27 + .../futures/tests/stream_futures_ordered.rs | 84 + .../futures/tests/stream_futures_unordered.rs | 369 + .../futures/tests/stream_into_async_read.rs | 94 + vendor/futures/tests/stream_peekable.rs | 58 + vendor/futures/tests/stream_select_all.rs | 197 + .../futures/tests/stream_select_next_some.rs | 86 + vendor/futures/tests/stream_split.rs | 57 + vendor/futures/tests/stream_try_stream.rs | 38 + vendor/futures/tests/stream_unfold.rs | 32 + vendor/futures/tests/task_arc_wake.rs | 79 + vendor/futures/tests/task_atomic_waker.rs | 48 + vendor/futures/tests/test_macro.rs | 20 + vendor/futures/tests/try_join.rs | 35 + vendor/futures/tests_disabled/all.rs | 400 + vendor/futures/tests_disabled/bilock.rs | 102 + vendor/futures/tests_disabled/stream.rs | 369 + vendor/globwalk/.cargo-checksum.json | 1 - vendor/globwalk/Cargo.lock | 334 - vendor/globwalk/Cargo.toml | 43 - vendor/globwalk/LICENSE | 19 - vendor/globwalk/README.md | 46 - vendor/globwalk/appveyor.yml | 125 - vendor/globwalk/examples/list.rs | 35 - vendor/globwalk/src/doctests.rs | 20 - vendor/globwalk/src/lib.rs | 839 --- vendor/globwalk/tests/docs.rs | 10 - vendor/hashbrown-0.11.2/.cargo-checksum.json | 1 + vendor/hashbrown-0.11.2/CHANGELOG.md | 342 + vendor/hashbrown-0.11.2/Cargo.toml | 84 + .../LICENSE-APACHE | 0 vendor/hashbrown-0.11.2/LICENSE-MIT | 25 + vendor/hashbrown-0.11.2/README.md | 126 + vendor/hashbrown-0.11.2/benches/bench.rs | 331 + vendor/hashbrown-0.11.2/clippy.toml | 1 + .../src/external_trait_impls/mod.rs | 4 + .../src/external_trait_impls/rayon/helpers.rs | 26 + .../src/external_trait_impls/rayon/map.rs | 734 ++ .../src/external_trait_impls/rayon/mod.rs | 4 + .../src/external_trait_impls/rayon/raw.rs | 229 + .../src/external_trait_impls/rayon/set.rs | 659 ++ .../src/external_trait_impls/serde.rs | 200 + vendor/hashbrown-0.11.2/src/lib.rs | 161 + vendor/hashbrown-0.11.2/src/macros.rs | 69 + vendor/hashbrown-0.11.2/src/map.rs | 4922 ++++++++++++ vendor/hashbrown-0.11.2/src/raw/alloc.rs | 72 + vendor/hashbrown-0.11.2/src/raw/bitmask.rs | 122 + vendor/hashbrown-0.11.2/src/raw/generic.rs | 151 + vendor/hashbrown-0.11.2/src/raw/mod.rs | 2262 ++++++ vendor/hashbrown-0.11.2/src/raw/sse2.rs | 145 + vendor/hashbrown-0.11.2/src/rustc_entry.rs | 630 ++ vendor/hashbrown-0.11.2/src/scopeguard.rs | 49 + vendor/hashbrown-0.11.2/src/set.rs | 2299 ++++++ vendor/hashbrown-0.11.2/tests/hasher.rs | 65 + vendor/hashbrown-0.11.2/tests/rayon.rs | 533 ++ vendor/hashbrown-0.11.2/tests/serde.rs | 65 + vendor/hashbrown-0.11.2/tests/set.rs | 30 + vendor/hashbrown/.cargo-checksum.json | 2 +- vendor/hashbrown/CHANGELOG.md | 37 +- vendor/hashbrown/Cargo.toml | 18 +- vendor/hashbrown/README.md | 8 +- vendor/hashbrown/benches/bench.rs | 2 +- .../benches/insert_unique_unchecked.rs | 32 + .../src/external_trait_impls/rayon/helpers.rs | 1 + .../src/external_trait_impls/rayon/map.rs | 4 +- .../src/external_trait_impls/rayon/raw.rs | 14 +- .../src/external_trait_impls/serde.rs | 1 + vendor/hashbrown/src/lib.rs | 17 +- vendor/hashbrown/src/macros.rs | 4 +- vendor/hashbrown/src/map.rs | 1845 ++++- vendor/hashbrown/src/raw/alloc.rs | 3 +- vendor/hashbrown/src/raw/bitmask.rs | 2 +- vendor/hashbrown/src/raw/generic.rs | 7 +- vendor/hashbrown/src/raw/mod.rs | 656 +- vendor/hashbrown/src/raw/sse2.rs | 1 + vendor/hashbrown/src/rustc_entry.rs | 8 +- vendor/hashbrown/src/scopeguard.rs | 2 +- vendor/hashbrown/src/set.rs | 78 +- vendor/hashbrown/tests/rayon.rs | 70 +- vendor/hashbrown/tests/set.rs | 34 +- vendor/indexmap/.cargo-checksum.json | 2 +- vendor/indexmap/Cargo.toml | 17 +- vendor/indexmap/README.rst | 318 +- vendor/indexmap/RELEASES.rst | 347 + vendor/indexmap/build.rs | 1 + vendor/indexmap/src/lib.rs | 6 + vendor/indexmap/src/macros.rs | 58 + vendor/indexmap/src/map.rs | 274 +- vendor/indexmap/src/map/core.rs | 15 +- vendor/indexmap/src/mutable_keys.rs | 4 +- vendor/indexmap/src/rayon/map.rs | 91 +- vendor/indexmap/src/rayon/mod.rs | 52 - vendor/indexmap/src/rayon/set.rs | 72 +- vendor/indexmap/src/rustc.rs | 158 + vendor/indexmap/src/set.rs | 159 +- vendor/libc/.cargo-checksum.json | 2 +- vendor/libc/Cargo.toml | 2 +- vendor/libc/LICENSE-APACHE | 25 - vendor/libc/src/fuchsia/mod.rs | 1 + vendor/libc/src/solid/aarch64.rs | 2 +- vendor/libc/src/solid/arm.rs | 2 +- vendor/libc/src/unix/bsd/apple/mod.rs | 72 + .../src/unix/bsd/freebsdlike/dragonfly/mod.rs | 12 +- .../src/unix/bsd/freebsdlike/freebsd/mod.rs | 215 +- .../bsd/freebsdlike/freebsd/x86_64/mod.rs | 63 + vendor/libc/src/unix/bsd/freebsdlike/mod.rs | 53 +- .../src/unix/bsd/netbsdlike/netbsd/mod.rs | 66 + .../src/unix/bsd/netbsdlike/openbsd/mod.rs | 16 +- .../linux_like/android/b64/aarch64/mod.rs | 7 + .../libc/src/unix/linux_like/android/mod.rs | 56 +- .../unix/linux_like/linux/arch/generic/mod.rs | 114 +- .../unix/linux_like/linux/arch/mips/mod.rs | 80 +- .../unix/linux_like/linux/arch/powerpc/mod.rs | 81 +- .../unix/linux_like/linux/arch/sparc/mod.rs | 74 +- .../unix/linux_like/linux/gnu/b32/arm/mod.rs | 46 +- .../unix/linux_like/linux/gnu/b32/mips/mod.rs | 32 - .../src/unix/linux_like/linux/gnu/b32/mod.rs | 10 +- .../unix/linux_like/linux/gnu/b32/powerpc.rs | 35 - .../linux_like/linux/gnu/b32/riscv32/mod.rs | 32 - .../linux_like/linux/gnu/b32/sparc/mod.rs | 33 - .../unix/linux_like/linux/gnu/b32/x86/mod.rs | 46 +- .../linux_like/linux/gnu/b64/aarch64/mod.rs | 58 +- .../linux_like/linux/gnu/b64/mips64/mod.rs | 35 +- .../linux_like/linux/gnu/b64/powerpc64/mod.rs | 44 +- .../linux_like/linux/gnu/b64/riscv64/mod.rs | 39 +- .../unix/linux_like/linux/gnu/b64/s390x.rs | 35 +- .../linux_like/linux/gnu/b64/sparc64/mod.rs | 38 +- .../linux_like/linux/gnu/b64/x86_64/mod.rs | 50 +- .../libc/src/unix/linux_like/linux/gnu/mod.rs | 190 +- vendor/libc/src/unix/linux_like/linux/mod.rs | 94 +- .../unix/linux_like/linux/musl/b32/arm/mod.rs | 34 - .../unix/linux_like/linux/musl/b32/hexagon.rs | 64 - .../linux_like/linux/musl/b32/mips/mod.rs | 34 - .../src/unix/linux_like/linux/musl/b32/mod.rs | 6 - .../unix/linux_like/linux/musl/b32/powerpc.rs | 34 - .../unix/linux_like/linux/musl/b32/x86/mod.rs | 34 - .../linux_like/linux/musl/b64/aarch64/mod.rs | 44 +- .../unix/linux_like/linux/musl/b64/mips64.rs | 37 +- .../linux_like/linux/musl/b64/powerpc64.rs | 36 - .../linux_like/linux/musl/b64/riscv64/mod.rs | 61 +- .../unix/linux_like/linux/musl/b64/s390x.rs | 36 - .../linux_like/linux/musl/b64/x86_64/mod.rs | 37 - .../src/unix/linux_like/linux/musl/mod.rs | 11 +- .../unix/linux_like/linux/uclibc/arm/mod.rs | 47 +- .../linux/uclibc/mips/mips32/mod.rs | 1 - .../linux/uclibc/mips/mips64/mod.rs | 5 - .../unix/linux_like/linux/uclibc/mips/mod.rs | 7 - .../src/unix/linux_like/linux/uclibc/mod.rs | 92 +- .../linux_like/linux/uclibc/x86_64/mod.rs | 1 + vendor/libc/src/unix/linux_like/mod.rs | 121 +- vendor/libc/src/unix/newlib/horizon/mod.rs | 194 + vendor/libc/src/unix/newlib/mod.rs | 38 +- vendor/libc/src/unix/solarish/illumos.rs | 18 + vendor/libc/src/unix/solarish/mod.rs | 52 + vendor/libloading/.cargo-checksum.json | 2 +- vendor/libloading/Cargo.toml | 4 +- vendor/libloading/src/changelog.rs | 10 + vendor/libloading/src/lib.rs | 6 +- vendor/libloading/src/os/mod.rs | 8 +- vendor/libloading/src/os/unix/consts.rs | 4 +- vendor/libloading/src/os/unix/mod.rs | 4 +- vendor/libloading/src/os/windows/mod.rs | 4 +- vendor/libloading/src/safe.rs | 10 +- vendor/lock_api/.cargo-checksum.json | 2 +- vendor/lock_api/Cargo.toml | 2 +- vendor/lock_api/src/mutex.rs | 6 +- vendor/lock_api/src/remutex.rs | 39 +- vendor/lock_api/src/rwlock.rs | 111 +- vendor/markup5ever/.cargo-checksum.json | 2 +- vendor/markup5ever/Cargo.toml | 11 +- vendor/markup5ever/build.rs | 44 +- vendor/markup5ever/data/entities.json | 2233 ------ vendor/markup5ever/entities.rs | 2233 ++++++ vendor/markup5ever/interface/mod.rs | 120 +- vendor/markup5ever/local_names.txt | 7 + vendor/markup5ever/serialize.rs | 2 +- vendor/markup5ever/util/buffer_queue.rs | 10 +- vendor/maybe-uninit/.cargo-checksum.json | 1 - vendor/maybe-uninit/README.md | 19 - vendor/maybe-uninit/build.rs | 50 - vendor/maybe-uninit/src/lib.rs | 10 - vendor/maybe-uninit/src/maybe_uninit.rs | 594 -- vendor/maybe-uninit/tests/doesnt_drop.rs | 37 - vendor/md-5/.cargo-checksum.json | 2 +- vendor/md-5/CHANGELOG.md | 6 + vendor/md-5/Cargo.lock | 114 - vendor/md-5/Cargo.toml | 23 +- vendor/md-5/README.md | 3 + vendor/md-5/benches/lib.rs | 4 - vendor/md-5/benches/mod.rs | 14 + vendor/md-5/examples/md5sum.rs | 47 - vendor/md-5/src/{utils.rs => compress.rs} | 27 +- vendor/md-5/src/consts.rs | 21 - vendor/md-5/src/lib.rs | 142 +- vendor/md-5/tests/data/md5.blb | Bin 3286 -> 3271 bytes vendor/md-5/tests/data/one_million_a.bin | 1 - vendor/md-5/tests/lib.rs | 12 - vendor/md-5/tests/mod.rs | 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/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/minifier/.cargo-checksum.json | 2 +- vendor/minifier/Cargo.lock | 34 +- vendor/minifier/Cargo.toml | 11 +- vendor/minifier/src/css/mod.rs | 22 +- vendor/minifier/src/css/tests.rs | 29 +- vendor/minifier/src/css/token.rs | 66 +- vendor/minifier/src/html.rs | 22 +- vendor/minifier/src/js/mod.rs | 22 +- vendor/minifier/src/js/token.rs | 23 +- vendor/minifier/src/js/tools.rs | 25 +- vendor/minifier/src/js/utils.rs | 24 +- vendor/minifier/src/json/json_minifier.rs | 2 + vendor/minifier/src/json/mod.rs | 2 + vendor/minifier/src/json/read/byte_to_char.rs | 2 + .../minifier/src/json/read/internal_buffer.rs | 2 + .../minifier/src/json/read/internal_reader.rs | 2 + vendor/minifier/src/json/read/json_read.rs | 2 + vendor/minifier/src/json/string.rs | 2 + vendor/minifier/src/lib.rs | 22 +- vendor/minifier/src/main.rs | 22 +- vendor/minifier/tests/js_minify.rs | 2 + vendor/minimal-lexical/.cargo-checksum.json | 1 + vendor/minimal-lexical/CHANGELOG | 38 + vendor/minimal-lexical/CODE_OF_CONDUCT.md | 141 + vendor/minimal-lexical/Cargo.toml | 33 + vendor/minimal-lexical/LICENSE-APACHE | 201 + .../LICENSE-MIT | 0 vendor/minimal-lexical/LICENSE.md | 37 + vendor/minimal-lexical/README.md | 102 + vendor/minimal-lexical/clippy.toml | 1 + vendor/minimal-lexical/rustfmt.toml | 16 + vendor/minimal-lexical/src/bellerophon.rs | 391 + vendor/minimal-lexical/src/bigint.rs | 788 ++ vendor/minimal-lexical/src/extended_float.rs | 24 + vendor/minimal-lexical/src/fpu.rs | 98 + vendor/minimal-lexical/src/heapvec.rs | 190 + vendor/minimal-lexical/src/lemire.rs | 225 + vendor/minimal-lexical/src/lib.rs | 68 + vendor/minimal-lexical/src/libm.rs | 1238 +++ vendor/minimal-lexical/src/mask.rs | 60 + vendor/minimal-lexical/src/num.rs | 308 + vendor/minimal-lexical/src/number.rs | 83 + vendor/minimal-lexical/src/parse.rs | 201 + vendor/minimal-lexical/src/rounding.rs | 131 + vendor/minimal-lexical/src/slow.rs | 403 + vendor/minimal-lexical/src/stackvec.rs | 308 + vendor/minimal-lexical/src/table.rs | 11 + .../minimal-lexical/src/table_bellerophon.rs | 119 + vendor/minimal-lexical/src/table_lemire.rs | 676 ++ vendor/minimal-lexical/src/table_small.rs | 90 + vendor/minimal-lexical/tests/bellerophon.rs | 59 + .../tests/bellerophon_tests.rs | 231 + .../tests/integration_tests.rs | 228 + vendor/minimal-lexical/tests/lemire_tests.rs | 378 + vendor/minimal-lexical/tests/libm_tests.rs | 289 + vendor/minimal-lexical/tests/mask_tests.rs | 16 + vendor/minimal-lexical/tests/number_tests.rs | 88 + vendor/minimal-lexical/tests/parse_tests.rs | 189 + .../minimal-lexical/tests/rounding_tests.rs | 64 + vendor/minimal-lexical/tests/slow_tests.rs | 337 + vendor/minimal-lexical/tests/stackvec.rs | 32 + vendor/minimal-lexical/tests/vec_tests.rs | 395 + vendor/nom/.cargo-checksum.json | 1 + vendor/nom/CHANGELOG.md | 1482 ++++ vendor/nom/Cargo.lock | 289 + vendor/nom/Cargo.toml | 130 + vendor/nom/LICENSE | 20 + vendor/nom/README.md | 309 + vendor/nom/build.rs | 7 + vendor/nom/doc/nom_recipes.md | 395 + vendor/nom/src/bits/complete.rs | 150 + vendor/nom/src/bits/mod.rs | 179 + vendor/nom/src/bits/streaming.rs | 129 + vendor/nom/src/branch/mod.rs | 291 + vendor/nom/src/branch/tests.rs | 142 + vendor/nom/src/bytes/complete.rs | 744 ++ vendor/nom/src/bytes/mod.rs | 6 + vendor/nom/src/bytes/streaming.rs | 700 ++ vendor/nom/src/bytes/tests.rs | 636 ++ vendor/nom/src/character/complete.rs | 1209 +++ vendor/nom/src/character/mod.rs | 116 + vendor/nom/src/character/streaming.rs | 1182 +++ vendor/nom/src/character/tests.rs | 62 + vendor/nom/src/combinator/mod.rs | 768 ++ vendor/nom/src/combinator/tests.rs | 275 + vendor/nom/src/error.rs | 831 ++ vendor/nom/src/internal.rs | 487 ++ vendor/nom/src/lib.rs | 467 ++ vendor/nom/src/multi/mod.rs | 981 +++ vendor/nom/src/multi/tests.rs | 544 ++ vendor/nom/src/number/complete.rs | 2143 ++++++ vendor/nom/src/number/mod.rs | 15 + vendor/nom/src/number/streaming.rs | 2223 ++++++ vendor/nom/src/sequence/mod.rs | 270 + vendor/nom/src/sequence/tests.rs | 274 + vendor/nom/src/str.rs | 536 ++ vendor/nom/src/traits.rs | 1425 ++++ vendor/nom/tests/arithmetic.rs | 94 + vendor/nom/tests/arithmetic_ast.rs | 161 + vendor/nom/tests/css.rs | 45 + vendor/nom/tests/custom_errors.rs | 48 + vendor/nom/tests/escaped.rs | 28 + vendor/nom/tests/float.rs | 46 + vendor/nom/tests/fnmut.rs | 39 + vendor/nom/tests/ini.rs | 207 + vendor/nom/tests/ini_str.rs | 217 + vendor/nom/tests/issues.rs | 208 + vendor/nom/tests/json.rs | 236 + vendor/nom/tests/mp4.rs | 320 + vendor/nom/tests/multiline.rs | 31 + vendor/nom/tests/overflow.rs | 145 + vendor/nom/tests/reborrow_fold.rs | 31 + vendor/num_cpus/.cargo-checksum.json | 2 +- vendor/num_cpus/CHANGELOG.md | 6 + vendor/num_cpus/Cargo.lock | 16 +- vendor/num_cpus/Cargo.toml | 17 +- vendor/num_cpus/README.md | 2 +- vendor/num_cpus/ci/cgroups/Dockerfile | 2 +- .../cgroups/proc/cgroups/mountinfo_multi_opt | 8 + .../cgroups/proc/cgroups/mountinfo_zero_opt | 8 + vendor/num_cpus/src/lib.rs | 5 +- vendor/num_cpus/src/linux.rs | 104 +- vendor/object-0.27.1/.cargo-checksum.json | 1 - .../src/read/macho/dyld_cache.rs | 218 - vendor/object/.cargo-checksum.json | 1 + vendor/{object-0.27.1 => object}/CHANGELOG.md | 89 +- vendor/{object-0.27.1 => object}/Cargo.toml | 27 +- .../LICENSE-APACHE | 0 vendor/{object-0.27.1 => object}/LICENSE-MIT | 0 vendor/{object-0.27.1 => object}/README.md | 0 vendor/{object-0.27.1 => object}/clippy.toml | 0 .../{object-0.27.1 => object}/src/archive.rs | 0 .../{object-0.27.1 => object}/src/common.rs | 27 + vendor/{object-0.27.1 => object}/src/elf.rs | 0 .../{object-0.27.1 => object}/src/endian.rs | 0 vendor/{object-0.27.1 => object}/src/lib.rs | 2 +- vendor/{object-0.27.1 => object}/src/macho.rs | 71 +- vendor/{object-0.27.1 => object}/src/pe.rs | 0 vendor/{object-0.27.1 => object}/src/pod.rs | 0 .../{object-0.27.1 => object}/src/read/any.rs | 38 +- .../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 | 28 +- .../src/read/coff/symbol.rs | 9 + .../src/read/elf/comdat.rs | 7 +- .../src/read/elf/compression.rs | 0 .../src/read/elf/dynamic.rs | 0 .../src/read/elf/file.rs | 0 .../src/read/elf/hash.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 | 8 +- .../src/read/elf/symbol.rs | 2 +- .../src/read/elf/version.rs | 0 vendor/object/src/read/macho/dyld_cache.rs | 343 + .../src/read/macho/fat.rs | 0 .../src/read/macho/file.rs | 102 +- .../src/read/macho/load_command.rs | 0 .../src/read/macho/mod.rs | 0 .../src/read/macho/relocation.rs | 0 .../src/read/macho/section.rs | 12 +- .../src/read/macho/segment.rs | 54 +- .../src/read/macho/symbol.rs | 0 .../{object-0.27.1 => object}/src/read/mod.rs | 0 .../src/read/pe/data_directory.rs | 21 +- .../src/read/pe/export.rs | 0 .../src/read/pe/file.rs | 11 +- .../src/read/pe/import.rs | 0 .../src/read/pe/mod.rs | 0 .../src/read/pe/relocation.rs | 0 .../src/read/pe/rich.rs | 0 .../src/read/pe/section.rs | 56 +- .../src/read/read_cache.rs | 0 .../src/read/read_ref.rs | 0 .../src/read/traits.rs | 7 +- .../src/read/util.rs | 0 .../src/read/wasm.rs | 55 +- .../src/write/coff.rs | 162 +- .../src/write/elf/mod.rs | 0 .../src/write/elf/object.rs | 3 +- .../src/write/elf/writer.rs | 23 +- .../src/write/macho.rs | 24 +- .../src/write/mod.rs | 18 +- .../{object-0.27.1 => object}/src/write/pe.rs | 11 +- .../src/write/string.rs | 8 +- .../src/write/util.rs | 9 +- .../tests/integration.rs | 1 + .../tests/parse_self.rs | 0 vendor/object/tests/read/coff.rs | 23 + vendor/object/tests/read/mod.rs | 3 + .../tests/round_trip/bss.rs | 0 vendor/object/tests/round_trip/coff.rs | 56 + .../tests/round_trip/comdat.rs | 2 +- .../tests/round_trip/common.rs | 0 .../tests/round_trip/elf.rs | 0 .../tests/round_trip/macho.rs | 0 .../tests/round_trip/mod.rs | 2 + .../object/tests/round_trip/section_flags.rs | 90 + .../tests/round_trip/tls.rs | 0 .../opaque-debug-0.2.3/.cargo-checksum.json | 1 - vendor/opaque-debug-0.2.3/src/lib.rs | 24 - vendor/opaque-debug/.cargo-checksum.json | 2 +- vendor/opaque-debug/Cargo.toml | 5 +- vendor/opaque-debug/src/lib.rs | 4 +- vendor/opaque-debug/tests/mod.rs | 13 - .../.cargo-checksum.json | 0 .../CHANGELOG.md | 0 .../Cargo.toml | 0 .../LICENSE-APACHE | 0 .../LICENSE-MIT | 0 .../README.md | 0 .../bors.toml | 0 .../src/condvar.rs | 0 .../src/deadlock.rs | 0 .../src/elision.rs | 0 .../src/fair_mutex.rs | 0 .../src/lib.rs | 0 .../src/mutex.rs | 0 .../src/once.rs | 0 .../src/raw_fair_mutex.rs | 0 .../src/raw_mutex.rs | 0 .../src/raw_rwlock.rs | 0 .../src/remutex.rs | 0 .../src/rwlock.rs | 0 .../src/util.rs | 0 .../tests/issue_203.rs | 0 .../.cargo-checksum.json | 0 .../Cargo.toml | 0 .../LICENSE-APACHE | 0 .../LICENSE-MIT | 0 .../build.rs | 0 .../src/lib.rs | 0 .../src/parking_lot.rs | 0 .../src/spinwait.rs | 0 .../src/thread_parker/generic.rs | 0 .../src/thread_parker/linux.rs | 0 .../src/thread_parker/mod.rs | 0 .../src/thread_parker/redox.rs | 0 .../src/thread_parker/sgx.rs | 0 .../src/thread_parker/unix.rs | 0 .../src/thread_parker/wasm.rs | 0 .../src/thread_parker/wasm_atomic.rs | 0 .../src/thread_parker/windows/keyed_event.rs | 0 .../src/thread_parker/windows/mod.rs | 0 .../src/thread_parker/windows/waitaddress.rs | 0 .../src/util.rs | 0 .../src/word_lock.rs | 0 vendor/pin-project-lite/.cargo-checksum.json | 2 +- vendor/pin-project-lite/CHANGELOG.md | 47 +- vendor/pin-project-lite/Cargo.toml | 16 +- vendor/pin-project-lite/src/lib.rs | 910 +-- vendor/pin-project-lite/tests/README.md | 46 + vendor/pin-project-lite/tests/compiletest.rs | 4 +- .../pin-project-lite/tests/expand/README.md | 17 - .../tests/expand/default/enum.expanded.rs | 10 +- .../tests/expand/multifields/enum.expanded.rs | 12 +- .../expand/multifields/struct.expanded.rs | 12 +- .../tests/expand/naming/enum-all.expanded.rs | 10 +- .../expand/naming/struct-all.expanded.rs | 10 +- vendor/pin-project-lite/tests/lint.rs | 26 +- vendor/pin-project-lite/tests/test.rs | 27 + .../tests/ui/pin_project/conflict-drop.stderr | 2 +- .../ui/pin_project/conflict-unpin.stderr | 6 +- .../ui/pin_project/invalid-bounds.stderr | 132 +- .../tests/ui/pin_project/invalid.stderr | 14 +- .../overlapping_lifetime_names.stderr | 54 +- .../overlapping_unpin_struct.stderr | 23 +- .../tests/ui/pin_project/packed.stderr | 10 +- .../tests/ui/pin_project/unpin_sneaky.stderr | 4 +- .../tests/ui/pin_project/unsupported.stderr | 26 +- .../ui/pinned_drop/call-drop-inner.stderr | 4 +- .../pinned_drop/conditional-drop-impl.stderr | 8 +- vendor/pin-utils/.cargo-checksum.json | 1 + .../Cargo.toml | 16 +- vendor/pin-utils/LICENSE-APACHE | 201 + vendor/pin-utils/LICENSE-MIT | 25 + vendor/pin-utils/README.md | 42 + vendor/pin-utils/src/lib.rs | 17 + vendor/pin-utils/src/projection.rs | 100 + vendor/pin-utils/src/stack_pin.rs | 25 + vendor/pin-utils/tests/projection.rs | 27 + vendor/pin-utils/tests/stack_pin.rs | 21 + vendor/pretty_assertions/.cargo-checksum.json | 2 +- vendor/pretty_assertions/CHANGELOG.md | 38 + vendor/pretty_assertions/Cargo.lock | 102 + vendor/pretty_assertions/Cargo.toml | 14 +- vendor/pretty_assertions/LICENSE-APACHE | 404 +- vendor/pretty_assertions/LICENSE-MIT | 42 +- vendor/pretty_assertions/README.md | 180 +- .../examples/pretty_assertion.png | Bin 27990 -> 21419 bytes .../examples/pretty_assertion.rs | 7 +- .../examples/pretty_assertion_v0_6_1.png | Bin 0 -> 21236 bytes .../examples/standard_assertion.png | Bin 23060 -> 21727 bytes .../examples/standard_assertion.rs | 42 +- vendor/pretty_assertions/scripts/check | 9 + vendor/pretty_assertions/scripts/install | 6 + vendor/pretty_assertions/scripts/publish | 11 + .../pretty_assertions/src/format_changeset.rs | 176 - vendor/pretty_assertions/src/lib.rs | 669 +- vendor/pretty_assertions/src/printer.rs | 474 ++ vendor/pretty_assertions/tests/assert_eq.rs | 195 - vendor/pretty_assertions/tests/assert_ne.rs | 174 - .../pretty_assertions/tests/pretty_string.rs | 27 - vendor/proc-macro2/.cargo-checksum.json | 2 +- vendor/proc-macro2/Cargo.toml | 3 +- vendor/proc-macro2/src/detection.rs | 8 +- vendor/proc-macro2/src/fallback.rs | 74 +- vendor/proc-macro2/src/lib.rs | 60 +- vendor/proc-macro2/src/parse.rs | 6 +- vendor/proc-macro2/src/wrapper.rs | 60 +- vendor/quote/.cargo-checksum.json | 2 +- vendor/quote/Cargo.toml | 7 +- vendor/quote/benches/bench.rs | 192 - vendor/quote/rust-toolchain.toml | 2 + vendor/quote/src/format.rs | 15 +- vendor/quote/src/lib.rs | 18 +- vendor/quote/src/runtime.rs | 97 +- vendor/quote/tests/test.rs | 35 +- ...does-not-have-iter-interpolated-dup.stderr | 4 +- .../ui/does-not-have-iter-interpolated.stderr | 4 +- .../ui/does-not-have-iter-separated.stderr | 4 +- .../quote/tests/ui/does-not-have-iter.stderr | 4 +- vendor/quote/tests/ui/not-quotable.stderr | 20 +- vendor/quote/tests/ui/not-repeatable.stderr | 68 +- vendor/quote/tests/ui/wrong-type-span.rs | 2 +- vendor/quote/tests/ui/wrong-type-span.stderr | 4 +- vendor/rustc-rayon-core/.cargo-checksum.json | 2 +- vendor/rustc-rayon-core/Cargo.toml | 18 +- vendor/rustc-rayon-core/src/job.rs | 14 +- vendor/rustc-rayon-core/src/registry.rs | 24 +- vendor/rustc-rayon/.cargo-checksum.json | 2 +- vendor/rustc-rayon/Cargo.lock | 158 +- vendor/rustc-rayon/Cargo.toml | 13 +- vendor/semver-0.11.0/.cargo-checksum.json | 1 - vendor/semver-0.11.0/Cargo.toml | 43 - vendor/semver-0.11.0/README.md | 103 - vendor/semver-0.11.0/src/diesel_impls.rs | 45 - vendor/semver-0.11.0/src/lib.rs | 223 - vendor/semver-0.11.0/src/version.rs | 873 --- vendor/semver-0.11.0/src/version_req.rs | 1092 --- vendor/semver-0.11.0/tests/deprecation.rs | 24 - vendor/semver-0.11.0/tests/diesel.rs | 209 - vendor/semver-0.11.0/tests/serde.rs | 93 - vendor/semver-parser/.cargo-checksum.json | 1 - vendor/semver-parser/Cargo.lock | 171 - vendor/semver-parser/Cargo.toml | 32 - vendor/semver-parser/README.md | 5 - vendor/semver-parser/src/generated.rs | 641 -- vendor/semver-parser/src/lexer.rs | 329 - vendor/semver-parser/src/lib.rs | 20 - vendor/semver-parser/src/main.rs | 15 - vendor/semver-parser/src/parser.rs | 262 - vendor/semver-parser/src/range.rs | 865 --- vendor/semver-parser/src/range_set.rs | 165 - vendor/semver-parser/src/semver.pest | 18 - vendor/semver-parser/src/version.rs | 511 -- vendor/semver-parser/tests/genpest.rs | 92 - vendor/semver/.cargo-checksum.json | 2 +- vendor/semver/Cargo.toml | 10 +- vendor/semver/src/error.rs | 8 + vendor/semver/src/lib.rs | 14 +- vendor/semver/src/parse.rs | 32 +- vendor/semver/tests/node/mod.rs | 2 +- vendor/semver/tests/test_identifier.rs | 7 + vendor/semver/tests/test_version.rs | 6 + vendor/semver/tests/test_version_req.rs | 39 +- vendor/serde/.cargo-checksum.json | 2 +- vendor/serde/Cargo.toml | 4 +- vendor/serde/README.md | 23 +- vendor/serde/build.rs | 89 +- vendor/serde/crates-io.md | 23 +- vendor/serde/src/de/format.rs | 30 + vendor/serde/src/de/impls.rs | 386 +- vendor/serde/src/de/mod.rs | 28 +- vendor/serde/src/integer128.rs | 4 +- vendor/serde/src/lib.rs | 21 +- vendor/serde/src/private/de.rs | 29 +- vendor/serde/src/private/mod.rs | 6 +- vendor/serde/src/ser/impls.rs | 21 +- vendor/serde/src/ser/mod.rs | 8 +- vendor/serde_derive/.cargo-checksum.json | 2 +- vendor/serde_derive/Cargo.toml | 2 +- vendor/serde_derive/README.md | 23 +- vendor/serde_derive/crates-io.md | 23 +- vendor/serde_derive/src/lib.rs | 2 +- vendor/serde_json/.cargo-checksum.json | 2 +- vendor/serde_json/CONTRIBUTING.md | 46 + vendor/serde_json/Cargo.toml | 30 +- vendor/serde_json/README.md | 23 +- vendor/serde_json/src/de.rs | 33 +- vendor/serde_json/src/error.rs | 9 +- vendor/serde_json/src/io/core.rs | 4 +- vendor/serde_json/src/lexical/bhcomp.rs | 2 +- vendor/serde_json/src/lexical/bignum.rs | 2 +- vendor/serde_json/src/lexical/math.rs | 5 +- vendor/serde_json/src/lexical/num.rs | 2 +- vendor/serde_json/src/lexical/rounding.rs | 2 +- vendor/serde_json/src/lexical/shift.rs | 2 +- vendor/serde_json/src/lib.rs | 74 +- vendor/serde_json/src/map.rs | 17 +- vendor/serde_json/src/number.rs | 6 +- vendor/serde_json/src/raw.rs | 11 +- vendor/serde_json/src/read.rs | 42 +- vendor/serde_json/src/ser.rs | 13 +- vendor/serde_json/src/value/de.rs | 10 +- vendor/serde_json/src/value/from.rs | 6 +- vendor/serde_json/src/value/index.rs | 9 +- vendor/serde_json/src/value/mod.rs | 8 +- vendor/serde_json/src/value/partial_eq.rs | 2 +- vendor/serde_json/src/value/ser.rs | 13 +- vendor/serde_json/tests/compiletest.rs | 7 + vendor/serde_json/tests/debug.rs | 68 + vendor/serde_json/tests/lexical.rs | 52 + vendor/serde_json/tests/lexical/algorithm.rs | 110 + vendor/serde_json/tests/lexical/exponent.rs | 54 + vendor/serde_json/tests/lexical/float.rs | 581 ++ vendor/serde_json/tests/lexical/math.rs | 211 + vendor/serde_json/tests/lexical/num.rs | 76 + vendor/serde_json/tests/lexical/parse.rs | 204 + vendor/serde_json/tests/lexical/rounding.rs | 316 + vendor/serde_json/tests/macros/mod.rs | 59 + vendor/serde_json/tests/map.rs | 47 + vendor/serde_json/tests/regression.rs | 3 + .../serde_json/tests/regression/issue520.rs | 20 + .../serde_json/tests/regression/issue795.rs | 57 + .../serde_json/tests/regression/issue845.rs | 72 + vendor/serde_json/tests/stream.rs | 182 + vendor/serde_json/tests/test.rs | 2386 ++++++ vendor/serde_json/tests/ui/missing_colon.rs | 5 + .../serde_json/tests/ui/missing_colon.stderr | 7 + vendor/serde_json/tests/ui/missing_comma.rs | 5 + .../serde_json/tests/ui/missing_comma.stderr | 7 + vendor/serde_json/tests/ui/missing_value.rs | 5 + .../serde_json/tests/ui/missing_value.stderr | 7 + vendor/serde_json/tests/ui/not_found.rs | 5 + vendor/serde_json/tests/ui/not_found.stderr | 5 + vendor/serde_json/tests/ui/parse_expr.rs | 5 + vendor/serde_json/tests/ui/parse_expr.stderr | 5 + vendor/serde_json/tests/ui/parse_key.rs | 5 + vendor/serde_json/tests/ui/parse_key.stderr | 5 + .../ui/unexpected_after_array_element.rs | 5 + .../ui/unexpected_after_array_element.stderr | 5 + .../tests/ui/unexpected_after_map_entry.rs | 5 + .../ui/unexpected_after_map_entry.stderr | 5 + .../serde_json/tests/ui/unexpected_colon.rs | 5 + .../tests/ui/unexpected_colon.stderr | 5 + .../serde_json/tests/ui/unexpected_comma.rs | 5 + .../tests/ui/unexpected_comma.stderr | 5 + vendor/sha-1/.cargo-checksum.json | 2 +- vendor/sha-1/CHANGELOG.md | 54 + vendor/sha-1/Cargo.lock | 135 - vendor/sha-1/Cargo.toml | 40 +- vendor/sha-1/README.md | 12 + vendor/sha-1/benches/lib.rs | 4 - vendor/sha-1/benches/mod.rs | 14 + vendor/sha-1/examples/sha1sum.rs | 47 - vendor/sha-1/src/compress.rs | 30 +- vendor/sha-1/src/compress/aarch64.rs | 27 +- vendor/sha-1/src/compress/soft.rs | 21 +- vendor/sha-1/src/compress/x86.rs | 6 +- vendor/sha-1/src/consts.rs | 17 - vendor/sha-1/src/lib.rs | 139 +- vendor/sha-1/tests/data/one_million_a.bin | 1 - vendor/sha-1/tests/data/sha1.blb | Bin 240 -> 234 bytes vendor/sha-1/tests/lib.rs | 12 - vendor/sha-1/tests/mod.rs | 15 + vendor/sha2/.cargo-checksum.json | 2 +- vendor/sha2/CHANGELOG.md | 69 + vendor/sha2/Cargo.lock | 135 - vendor/sha2/Cargo.toml | 38 +- vendor/sha2/README.md | 3 + vendor/sha2/benches/mod.rs | 22 + vendor/sha2/benches/sha256.rs | 4 - vendor/sha2/benches/sha512.rs | 4 - vendor/sha2/examples/sha256sum.rs | 47 - vendor/sha2/examples/sha512sum.rs | 47 - vendor/sha2/src/consts.rs | 211 +- vendor/sha2/src/core_api.rs | 157 + vendor/sha2/src/lib.rs | 54 +- vendor/sha2/src/sha256.rs | 170 +- vendor/sha2/src/sha256/aarch64.rs | 19 +- vendor/sha2/src/sha256/soft.rs | 1 - vendor/sha2/src/sha256/x86.rs | 12 +- vendor/sha2/src/sha512.rs | 257 +- vendor/sha2/src/sha512/soft.rs | 1 - vendor/sha2/src/sha512/x86.rs | 357 + vendor/sha2/tests/data/sha224.blb | Bin 184 -> 178 bytes vendor/sha2/tests/data/sha256.blb | Bin 196 -> 190 bytes .../sha2/tests/data/sha256_one_million_a.bin | 1 - vendor/sha2/tests/data/sha384.blb | Bin 244 -> 238 bytes vendor/sha2/tests/data/sha512.blb | Bin 292 -> 289 bytes vendor/sha2/tests/data/sha512_224.blb | Bin 184 -> 178 bytes vendor/sha2/tests/data/sha512_256.blb | Bin 196 -> 190 bytes .../sha2/tests/data/sha512_one_million_a.bin | Bin 64 -> 0 bytes vendor/sha2/tests/lib.rs | 31 - vendor/sha2/tests/mod.rs | 35 + vendor/slab/.cargo-checksum.json | 1 + vendor/slab/CHANGELOG.md | 8 + vendor/{maybe-uninit => slab}/Cargo.toml | 20 +- vendor/slab/LICENSE | 25 + vendor/slab/README.md | 48 + vendor/slab/src/lib.rs | 977 +++ vendor/slab/tests/slab.rs | 301 + vendor/smallvec/.cargo-checksum.json | 2 +- vendor/smallvec/Cargo.toml | 6 +- vendor/smallvec/benches/bench.rs | 11 +- vendor/smallvec/src/arbitrary.rs | 19 + vendor/smallvec/src/lib.rs | 3 + vendor/smallvec/src/tests.rs | 7 + .../.cargo-checksum.json | 0 vendor/{strsim => strsim-0.8.0}/CHANGELOG.md | 0 vendor/{strsim => strsim-0.8.0}/Cargo.toml | 0 vendor/{strsim => strsim-0.8.0}/LICENSE | 0 vendor/{strsim => strsim-0.8.0}/README.md | 0 vendor/{strsim => strsim-0.8.0}/appveyor.yml | 0 .../benches/benches.rs | 0 vendor/{strsim => strsim-0.8.0}/dev | 0 vendor/{strsim => strsim-0.8.0}/src/lib.rs | 0 vendor/{strsim => strsim-0.8.0}/tests/lib.rs | 0 vendor/syn/.cargo-checksum.json | 2 +- vendor/syn/Cargo.toml | 5 +- vendor/syn/benches/file.rs | 1 + vendor/syn/benches/rust.rs | 3 +- vendor/syn/build.rs | 2 +- vendor/syn/src/buffer.rs | 60 +- vendor/syn/src/error.rs | 7 +- vendor/syn/src/expr.rs | 18 +- vendor/syn/src/gen/clone.rs | 10 +- vendor/syn/src/gen/debug.rs | 2 +- vendor/syn/src/gen/eq.rs | 417 +- vendor/syn/src/gen/fold.rs | 591 +- vendor/syn/src/gen/hash.rs | 17 +- vendor/syn/src/gen/visit.rs | 248 +- vendor/syn/src/gen/visit_mut.rs | 238 +- vendor/syn/src/item.rs | 2 +- vendor/syn/src/lib.rs | 7 +- vendor/syn/src/lit.rs | 39 +- vendor/syn/src/parse_quote.rs | 36 +- vendor/syn/src/stmt.rs | 9 +- vendor/syn/tests/common/eq.rs | 36 +- vendor/syn/tests/debug/gen.rs | 67 +- vendor/syn/tests/debug/mod.rs | 1 + vendor/syn/tests/macros/mod.rs | 12 +- vendor/syn/tests/regression.rs | 5 + vendor/syn/tests/regression/issue1108.rs | 5 + vendor/syn/tests/repo/mod.rs | 23 +- vendor/syn/tests/test_precedence.rs | 9 +- vendor/syn/tests/test_round_trip.rs | 7 +- vendor/tera/.cargo-checksum.json | 1 - vendor/tera/CHANGELOG.md | 466 -- vendor/tera/Cargo.toml | 88 - vendor/tera/LICENSE | 22 - vendor/tera/README.md | 31 - vendor/tera/docs/README.md | 4 - vendor/tera/src/builtins/filters/array.rs | 835 -- vendor/tera/src/builtins/filters/common.rs | 357 - vendor/tera/src/builtins/filters/mod.rs | 30 - vendor/tera/src/builtins/filters/number.rs | 181 - vendor/tera/src/builtins/filters/object.rs | 53 - vendor/tera/src/builtins/filters/string.rs | 848 --- vendor/tera/src/builtins/functions.rs | 341 - vendor/tera/src/builtins/mod.rs | 3 - vendor/tera/src/builtins/testers.rs | 348 - vendor/tera/src/context.rs | 287 - vendor/tera/src/errors.rs | 234 - vendor/tera/src/filter_utils.rs | 176 - vendor/tera/src/lib.rs | 50 - vendor/tera/src/macros.rs | 35 - vendor/tera/src/parser/ast.rs | 353 - vendor/tera/src/parser/mod.rs | 1223 --- vendor/tera/src/parser/tera.pest | 316 - vendor/tera/src/parser/tests/errors.rs | 290 - vendor/tera/src/parser/tests/lexer.rs | 688 -- vendor/tera/src/parser/tests/mod.rs | 4 - vendor/tera/src/parser/tests/parser.rs | 1114 --- vendor/tera/src/parser/tests/whitespace.rs | 264 - vendor/tera/src/parser/whitespace.rs | 188 - vendor/tera/src/renderer/call_stack.rs | 234 - vendor/tera/src/renderer/for_loop.rs | 181 - vendor/tera/src/renderer/macros.rs | 119 - vendor/tera/src/renderer/mod.rs | 62 - vendor/tera/src/renderer/processor.rs | 1060 --- vendor/tera/src/renderer/square_brackets.rs | 51 - vendor/tera/src/renderer/stack_frame.rs | 202 - vendor/tera/src/renderer/tests/basic.rs | 972 --- vendor/tera/src/renderer/tests/errors.rs | 284 - vendor/tera/src/renderer/tests/inheritance.rs | 176 - vendor/tera/src/renderer/tests/macros.rs | 356 - vendor/tera/src/renderer/tests/mod.rs | 32 - .../src/renderer/tests/square_brackets.rs | 94 - vendor/tera/src/renderer/tests/whitespace.rs | 118 - vendor/tera/src/template.rs | 170 - vendor/tera/src/tera.rs | 1183 --- vendor/tera/src/utils.rs | 85 - .../.cargo-checksum.json | 0 .../{textwrap => textwrap-0.11.0}/Cargo.toml | 0 vendor/{textwrap => textwrap-0.11.0}/LICENSE | 0 .../{textwrap => textwrap-0.11.0}/README.md | 0 .../benches/linear.rs | 0 .../examples/layout.rs | 0 .../examples/termwidth.rs | 0 .../src/indentation.rs | 0 .../{textwrap => textwrap-0.11.0}/src/lib.rs | 0 .../src/splitting.rs | 0 .../tests/version-numbers.rs | 0 vendor/thorin-dwp/.cargo-checksum.json | 2 +- vendor/thorin-dwp/Cargo.toml | 10 +- vendor/thorin-dwp/README.md | 12 +- vendor/thorin-dwp/src/index.rs | 212 +- vendor/thorin-dwp/src/package.rs | 6 +- vendor/thorin-dwp/src/relocate.rs | 3 +- vendor/thorin-dwp/src/strings.rs | 54 +- vendor/thread_local/.cargo-checksum.json | 2 +- vendor/thread_local/Cargo.toml | 11 +- vendor/thread_local/src/lib.rs | 2 +- vendor/tokio/.cargo-checksum.json | 1 + vendor/tokio/CHANGELOG.md | 1596 ++++ vendor/tokio/Cargo.toml | 131 + vendor/tokio/LICENSE | 25 + vendor/tokio/README.md | 172 + vendor/tokio/build.rs | 22 + vendor/tokio/docs/reactor-refactor.md | 276 + vendor/tokio/src/blocking.rs | 48 + vendor/tokio/src/coop.rs | 285 + vendor/tokio/src/doc/mod.rs | 23 + vendor/tokio/src/doc/os.rs | 26 + vendor/tokio/src/doc/winapi.rs | 66 + vendor/tokio/src/fs/canonicalize.rs | 51 + vendor/tokio/src/fs/copy.rs | 27 + vendor/tokio/src/fs/create_dir.rs | 52 + vendor/tokio/src/fs/create_dir_all.rs | 53 + vendor/tokio/src/fs/dir_builder.rs | 137 + vendor/tokio/src/fs/file.rs | 774 ++ vendor/tokio/src/fs/file/tests.rs | 955 +++ vendor/tokio/src/fs/hard_link.rs | 46 + vendor/tokio/src/fs/metadata.rs | 47 + vendor/tokio/src/fs/mocks.rs | 136 + vendor/tokio/src/fs/mod.rs | 126 + vendor/tokio/src/fs/open_options.rs | 665 ++ .../src/fs/open_options/mock_open_options.rs | 38 + vendor/tokio/src/fs/read.rs | 47 + vendor/tokio/src/fs/read_dir.rs | 286 + vendor/tokio/src/fs/read_link.rs | 14 + vendor/tokio/src/fs/read_to_string.rs | 26 + vendor/tokio/src/fs/remove_dir.rs | 12 + vendor/tokio/src/fs/remove_dir_all.rs | 14 + vendor/tokio/src/fs/remove_file.rs | 18 + vendor/tokio/src/fs/rename.rs | 17 + vendor/tokio/src/fs/set_permissions.rs | 15 + vendor/tokio/src/fs/symlink.rs | 18 + vendor/tokio/src/fs/symlink_dir.rs | 19 + vendor/tokio/src/fs/symlink_file.rs | 19 + vendor/tokio/src/fs/symlink_metadata.rs | 15 + vendor/tokio/src/fs/write.rs | 27 + vendor/tokio/src/future/block_on.rs | 15 + vendor/tokio/src/future/maybe_done.rs | 76 + vendor/tokio/src/future/mod.rs | 35 + vendor/tokio/src/future/poll_fn.rs | 40 + vendor/tokio/src/future/ready.rs | 27 + vendor/tokio/src/future/trace.rs | 11 + vendor/tokio/src/future/try_join.rs | 82 + vendor/tokio/src/io/async_buf_read.rs | 117 + vendor/tokio/src/io/async_fd.rs | 659 ++ vendor/tokio/src/io/async_read.rs | 131 + vendor/tokio/src/io/async_seek.rs | 90 + vendor/tokio/src/io/async_write.rs | 408 + vendor/tokio/src/io/blocking.rs | 279 + vendor/tokio/src/io/driver/interest.rs | 112 + vendor/tokio/src/io/driver/mod.rs | 353 + vendor/tokio/src/io/driver/platform.rs | 44 + vendor/tokio/src/io/driver/ready.rs | 239 + vendor/tokio/src/io/driver/registration.rs | 262 + vendor/tokio/src/io/driver/scheduled_io.rs | 544 ++ vendor/tokio/src/io/mod.rs | 267 + vendor/tokio/src/io/poll_evented.rs | 210 + vendor/tokio/src/io/read_buf.rs | 285 + vendor/tokio/src/io/seek.rs | 57 + vendor/tokio/src/io/split.rs | 180 + vendor/tokio/src/io/stderr.rs | 109 + vendor/tokio/src/io/stdin.rs | 73 + vendor/tokio/src/io/stdio_common.rs | 220 + vendor/tokio/src/io/stdout.rs | 108 + .../tokio/src/io/util/async_buf_read_ext.rs | 259 + vendor/tokio/src/io/util/async_read_ext.rs | 1141 +++ vendor/tokio/src/io/util/async_seek_ext.rs | 83 + vendor/tokio/src/io/util/async_write_ext.rs | 1141 +++ vendor/tokio/src/io/util/buf_reader.rs | 312 + vendor/tokio/src/io/util/buf_stream.rs | 207 + vendor/tokio/src/io/util/buf_writer.rs | 310 + vendor/tokio/src/io/util/chain.rs | 144 + vendor/tokio/src/io/util/copy.rs | 149 + .../tokio/src/io/util/copy_bidirectional.rs | 120 + vendor/tokio/src/io/util/copy_buf.rs | 102 + vendor/tokio/src/io/util/empty.rs | 84 + vendor/tokio/src/io/util/flush.rs | 46 + vendor/tokio/src/io/util/lines.rs | 143 + vendor/tokio/src/io/util/mem.rs | 243 + vendor/tokio/src/io/util/mod.rs | 96 + vendor/tokio/src/io/util/read.rs | 55 + vendor/tokio/src/io/util/read_buf.rs | 72 + vendor/tokio/src/io/util/read_exact.rs | 69 + vendor/tokio/src/io/util/read_int.rs | 153 + vendor/tokio/src/io/util/read_line.rs | 119 + vendor/tokio/src/io/util/read_to_end.rs | 112 + vendor/tokio/src/io/util/read_to_string.rs | 78 + vendor/tokio/src/io/util/read_until.rs | 79 + vendor/tokio/src/io/util/repeat.rs | 72 + vendor/tokio/src/io/util/shutdown.rs | 46 + vendor/tokio/src/io/util/sink.rs | 87 + vendor/tokio/src/io/util/split.rs | 121 + vendor/tokio/src/io/util/take.rs | 133 + .../tokio/src/io/util/vec_with_initialized.rs | 132 + vendor/tokio/src/io/util/write.rs | 46 + vendor/tokio/src/io/util/write_all.rs | 55 + vendor/tokio/src/io/util/write_all_buf.rs | 56 + vendor/tokio/src/io/util/write_buf.rs | 55 + vendor/tokio/src/io/util/write_int.rs | 146 + vendor/tokio/src/io/util/write_vectored.rs | 47 + vendor/tokio/src/lib.rs | 511 ++ vendor/tokio/src/loom/mocked.rs | 40 + vendor/tokio/src/loom/mod.rs | 14 + vendor/tokio/src/loom/std/atomic_ptr.rs | 34 + vendor/tokio/src/loom/std/atomic_u16.rs | 44 + vendor/tokio/src/loom/std/atomic_u32.rs | 34 + vendor/tokio/src/loom/std/atomic_u64.rs | 75 + vendor/tokio/src/loom/std/atomic_u8.rs | 34 + vendor/tokio/src/loom/std/atomic_usize.rs | 56 + vendor/tokio/src/loom/std/mod.rs | 96 + vendor/tokio/src/loom/std/mutex.rs | 31 + vendor/tokio/src/loom/std/parking_lot.rs | 106 + vendor/tokio/src/loom/std/unsafe_cell.rs | 16 + vendor/tokio/src/macros/cfg.rs | 386 + vendor/tokio/src/macros/join.rs | 119 + vendor/tokio/src/macros/loom.rs | 12 + vendor/tokio/src/macros/mod.rs | 35 + vendor/tokio/src/macros/pin.rs | 144 + vendor/tokio/src/macros/ready.rs | 8 + vendor/tokio/src/macros/scoped_tls.rs | 77 + vendor/tokio/src/macros/select.rs | 1001 +++ vendor/tokio/src/macros/support.rs | 9 + vendor/tokio/src/macros/thread_local.rs | 4 + vendor/tokio/src/macros/try_join.rs | 132 + vendor/tokio/src/net/addr.rs | 319 + vendor/tokio/src/net/lookup_host.rs | 38 + vendor/tokio/src/net/mod.rs | 52 + vendor/tokio/src/net/tcp/listener.rs | 397 + vendor/tokio/src/net/tcp/mod.rs | 14 + vendor/tokio/src/net/tcp/socket.rs | 589 ++ vendor/tokio/src/net/tcp/split.rs | 192 + vendor/tokio/src/net/tcp/split_owned.rs | 278 + vendor/tokio/src/net/tcp/stream.rs | 1293 ++++ vendor/tokio/src/net/udp.rs | 1484 ++++ vendor/tokio/src/net/unix/datagram/mod.rs | 3 + vendor/tokio/src/net/unix/datagram/socket.rs | 1321 ++++ vendor/tokio/src/net/unix/listener.rs | 186 + vendor/tokio/src/net/unix/mod.rs | 24 + vendor/tokio/src/net/unix/socketaddr.rs | 31 + vendor/tokio/src/net/unix/split.rs | 100 + vendor/tokio/src/net/unix/split_owned.rs | 191 + vendor/tokio/src/net/unix/stream.rs | 895 +++ vendor/tokio/src/net/unix/ucred.rs | 214 + vendor/tokio/src/net/windows/mod.rs | 3 + vendor/tokio/src/net/windows/named_pipe.rs | 2180 ++++++ vendor/tokio/src/park/either.rs | 74 + vendor/tokio/src/park/mod.rs | 117 + vendor/tokio/src/park/thread.rs | 346 + vendor/tokio/src/process/kill.rs | 13 + vendor/tokio/src/process/mod.rs | 1393 ++++ vendor/tokio/src/process/unix/driver.rs | 58 + vendor/tokio/src/process/unix/mod.rs | 257 + vendor/tokio/src/process/unix/orphan.rs | 320 + vendor/tokio/src/process/unix/reap.rs | 298 + vendor/tokio/src/process/windows.rs | 205 + vendor/tokio/src/runtime/basic_scheduler.rs | 534 ++ vendor/tokio/src/runtime/blocking/mod.rs | 42 + vendor/tokio/src/runtime/blocking/pool.rs | 352 + vendor/tokio/src/runtime/blocking/schedule.rs | 24 + vendor/tokio/src/runtime/blocking/shutdown.rs | 71 + vendor/tokio/src/runtime/blocking/task.rs | 44 + vendor/tokio/src/runtime/builder.rs | 593 ++ vendor/tokio/src/runtime/context.rs | 73 + vendor/tokio/src/runtime/driver.rs | 208 + vendor/tokio/src/runtime/enter.rs | 205 + vendor/tokio/src/runtime/handle.rs | 324 + vendor/tokio/src/runtime/mod.rs | 563 ++ vendor/tokio/src/runtime/park.rs | 257 + vendor/tokio/src/runtime/queue.rs | 646 ++ vendor/tokio/src/runtime/spawner.rs | 45 + vendor/tokio/src/runtime/task/core.rs | 341 + vendor/tokio/src/runtime/task/error.rs | 145 + vendor/tokio/src/runtime/task/harness.rs | 481 ++ vendor/tokio/src/runtime/task/join.rs | 260 + vendor/tokio/src/runtime/task/mod.rs | 222 + vendor/tokio/src/runtime/task/raw.rs | 145 + vendor/tokio/src/runtime/task/stack.rs | 83 + vendor/tokio/src/runtime/task/state.rs | 458 ++ vendor/tokio/src/runtime/task/waker.rs | 130 + .../src/runtime/tests/loom_basic_scheduler.rs | 82 + .../tokio/src/runtime/tests/loom_blocking.rs | 31 + vendor/tokio/src/runtime/tests/loom_local.rs | 47 + .../tokio/src/runtime/tests/loom_oneshot.rs | 48 + vendor/tokio/src/runtime/tests/loom_pool.rs | 379 + vendor/tokio/src/runtime/tests/loom_queue.rs | 216 + .../src/runtime/tests/loom_shutdown_join.rs | 28 + vendor/tokio/src/runtime/tests/mod.rs | 40 + vendor/tokio/src/runtime/tests/queue.rs | 202 + vendor/tokio/src/runtime/tests/task.rs | 159 + .../src/runtime/tests/task_combinations.rs | 380 + .../src/runtime/thread_pool/atomic_cell.rs | 51 + vendor/tokio/src/runtime/thread_pool/idle.rs | 222 + vendor/tokio/src/runtime/thread_pool/mod.rs | 116 + .../tokio/src/runtime/thread_pool/worker.rs | 841 +++ vendor/tokio/src/signal/ctrl_c.rs | 53 + vendor/tokio/src/signal/mod.rs | 100 + vendor/tokio/src/signal/registry.rs | 274 + vendor/tokio/src/signal/reusable_box.rs | 227 + vendor/tokio/src/signal/unix.rs | 476 ++ vendor/tokio/src/signal/unix/driver.rs | 207 + vendor/tokio/src/signal/windows.rs | 375 + vendor/tokio/src/sync/barrier.rs | 143 + vendor/tokio/src/sync/batch_semaphore.rs | 589 ++ vendor/tokio/src/sync/broadcast.rs | 1078 +++ vendor/tokio/src/sync/mod.rs | 499 ++ vendor/tokio/src/sync/mpsc/block.rs | 387 + vendor/tokio/src/sync/mpsc/bounded.rs | 1140 +++ vendor/tokio/src/sync/mpsc/chan.rs | 357 + vendor/tokio/src/sync/mpsc/error.rs | 101 + vendor/tokio/src/sync/mpsc/list.rs | 339 + vendor/tokio/src/sync/mpsc/mod.rs | 98 + vendor/tokio/src/sync/mpsc/unbounded.rs | 323 + vendor/tokio/src/sync/mutex.rs | 725 ++ vendor/tokio/src/sync/notify.rs | 741 ++ vendor/tokio/src/sync/once_cell.rs | 406 + vendor/tokio/src/sync/oneshot.rs | 1007 +++ vendor/tokio/src/sync/rwlock.rs | 727 ++ .../tokio/src/sync/rwlock/owned_read_guard.rs | 149 + .../src/sync/rwlock/owned_write_guard.rs | 234 + .../sync/rwlock/owned_write_guard_mapped.rs | 171 + vendor/tokio/src/sync/rwlock/read_guard.rs | 156 + vendor/tokio/src/sync/rwlock/write_guard.rs | 240 + .../src/sync/rwlock/write_guard_mapped.rs | 176 + vendor/tokio/src/sync/semaphore.rs | 564 ++ vendor/tokio/src/sync/task/atomic_waker.rs | 323 + vendor/tokio/src/sync/task/mod.rs | 4 + vendor/tokio/src/sync/tests/atomic_waker.rs | 34 + .../tokio/src/sync/tests/loom_atomic_waker.rs | 45 + vendor/tokio/src/sync/tests/loom_broadcast.rs | 207 + vendor/tokio/src/sync/tests/loom_list.rs | 48 + vendor/tokio/src/sync/tests/loom_mpsc.rs | 134 + vendor/tokio/src/sync/tests/loom_notify.rs | 140 + vendor/tokio/src/sync/tests/loom_oneshot.rs | 140 + vendor/tokio/src/sync/tests/loom_rwlock.rs | 105 + .../src/sync/tests/loom_semaphore_batch.rs | 215 + vendor/tokio/src/sync/tests/loom_watch.rs | 36 + vendor/tokio/src/sync/tests/mod.rs | 16 + .../tokio/src/sync/tests/semaphore_batch.rs | 250 + vendor/tokio/src/sync/watch.rs | 591 ++ vendor/tokio/src/task/blocking.rs | 143 + vendor/tokio/src/task/builder.rs | 105 + vendor/tokio/src/task/local.rs | 720 ++ vendor/tokio/src/task/mod.rs | 307 + vendor/tokio/src/task/spawn.rs | 143 + vendor/tokio/src/task/task_local.rs | 277 + vendor/tokio/src/task/unconstrained.rs | 45 + vendor/tokio/src/task/yield_now.rs | 38 + vendor/tokio/src/time/clock.rs | 207 + vendor/tokio/src/time/driver/entry.rs | 629 ++ vendor/tokio/src/time/driver/handle.rs | 88 + vendor/tokio/src/time/driver/mod.rs | 520 ++ vendor/tokio/src/time/driver/sleep.rs | 257 + vendor/tokio/src/time/driver/tests/mod.rs | 287 + vendor/tokio/src/time/driver/wheel/level.rs | 275 + vendor/tokio/src/time/driver/wheel/mod.rs | 359 + vendor/tokio/src/time/driver/wheel/stack.rs | 112 + vendor/tokio/src/time/error.rs | 120 + vendor/tokio/src/time/instant.rs | 231 + vendor/tokio/src/time/interval.rs | 447 ++ vendor/tokio/src/time/mod.rs | 114 + vendor/tokio/src/time/tests/mod.rs | 22 + vendor/tokio/src/time/tests/test_sleep.rs | 443 ++ vendor/tokio/src/time/timeout.rs | 159 + vendor/tokio/src/util/bit.rs | 77 + vendor/tokio/src/util/error.rs | 9 + vendor/tokio/src/util/linked_list.rs | 732 ++ vendor/tokio/src/util/mod.rs | 46 + vendor/tokio/src/util/pad.rs | 52 + vendor/tokio/src/util/rand.rs | 64 + vendor/tokio/src/util/slab.rs | 841 +++ vendor/tokio/src/util/trace.rs | 39 + vendor/tokio/src/util/try_lock.rs | 80 + vendor/tokio/src/util/wake.rs | 79 + vendor/tokio/tests/_require_full.rs | 2 + vendor/tokio/tests/async_send_sync.rs | 368 + vendor/tokio/tests/buffered.rs | 50 + vendor/tokio/tests/fs.rs | 20 + vendor/tokio/tests/fs_copy.rs | 39 + vendor/tokio/tests/fs_dir.rs | 87 + vendor/tokio/tests/fs_file.rs | 100 + vendor/tokio/tests/fs_link.rs | 68 + vendor/tokio/tests/io_async_fd.rs | 599 ++ vendor/tokio/tests/io_async_read.rs | 10 + vendor/tokio/tests/io_buf_reader.rs | 350 + vendor/tokio/tests/io_buf_writer.rs | 537 ++ vendor/tokio/tests/io_chain.rs | 16 + vendor/tokio/tests/io_copy.rs | 36 + vendor/tokio/tests/io_copy_bidirectional.rs | 128 + vendor/tokio/tests/io_driver.rs | 99 + vendor/tokio/tests/io_driver_drop.rs | 54 + vendor/tokio/tests/io_lines.rs | 19 + vendor/tokio/tests/io_mem_stream.rs | 102 + vendor/tokio/tests/io_read.rs | 59 + vendor/tokio/tests/io_read_buf.rs | 36 + vendor/tokio/tests/io_read_exact.rs | 15 + vendor/tokio/tests/io_read_line.rs | 107 + vendor/tokio/tests/io_read_to_end.rs | 78 + vendor/tokio/tests/io_read_to_string.rs | 63 + vendor/tokio/tests/io_read_until.rs | 74 + vendor/tokio/tests/io_split.rs | 79 + vendor/tokio/tests/io_take.rs | 16 + vendor/tokio/tests/io_write.rs | 58 + vendor/tokio/tests/io_write_all.rs | 51 + vendor/tokio/tests/io_write_all_buf.rs | 96 + vendor/tokio/tests/io_write_buf.rs | 56 + vendor/tokio/tests/io_write_int.rs | 37 + vendor/tokio/tests/macros_join.rs | 72 + vendor/tokio/tests/macros_pin.rs | 13 + vendor/tokio/tests/macros_select.rs | 549 ++ vendor/tokio/tests/macros_test.rs | 48 + vendor/tokio/tests/macros_try_join.rs | 101 + vendor/tokio/tests/named_pipe.rs | 393 + vendor/tokio/tests/net_bind_resource.rs | 14 + vendor/tokio/tests/net_lookup_host.rs | 36 + vendor/tokio/tests/no_rt.rs | 39 + vendor/tokio/tests/process_issue_2174.rs | 45 + vendor/tokio/tests/process_issue_42.rs | 38 + vendor/tokio/tests/process_kill_on_drop.rs | 42 + vendor/tokio/tests/process_smoke.rs | 34 + vendor/tokio/tests/rt_basic.rs | 158 + vendor/tokio/tests/rt_common.rs | 1109 +++ vendor/tokio/tests/rt_handle_block_on.rs | 533 ++ vendor/tokio/tests/rt_threaded.rs | 478 ++ vendor/tokio/tests/signal_ctrl_c.rs | 30 + vendor/tokio/tests/signal_drop_recv.rs | 22 + vendor/tokio/tests/signal_drop_rt.rs | 44 + vendor/tokio/tests/signal_drop_signal.rs | 26 + vendor/tokio/tests/signal_multi_rt.rs | 54 + vendor/tokio/tests/signal_no_rt.rs | 11 + vendor/tokio/tests/signal_notify_both.rs | 23 + vendor/tokio/tests/signal_twice.rs | 22 + vendor/tokio/tests/signal_usr1.rs | 23 + vendor/tokio/tests/support/io_vec.rs | 45 + vendor/tokio/tests/support/mpsc_stream.rs | 42 + vendor/tokio/tests/support/signal.rs | 7 + vendor/tokio/tests/sync_barrier.rs | 96 + vendor/tokio/tests/sync_broadcast.rs | 456 ++ vendor/tokio/tests/sync_errors.rs | 27 + vendor/tokio/tests/sync_mpsc.rs | 496 ++ vendor/tokio/tests/sync_mutex.rs | 164 + vendor/tokio/tests/sync_mutex_owned.rs | 122 + vendor/tokio/tests/sync_notify.rs | 153 + vendor/tokio/tests/sync_once_cell.rs | 274 + vendor/tokio/tests/sync_oneshot.rs | 269 + vendor/tokio/tests/sync_rwlock.rs | 270 + vendor/tokio/tests/sync_semaphore.rs | 95 + vendor/tokio/tests/sync_semaphore_owned.rs | 106 + vendor/tokio/tests/sync_watch.rs | 188 + vendor/tokio/tests/task_abort.rs | 224 + vendor/tokio/tests/task_blocking.rs | 228 + vendor/tokio/tests/task_builder.rs | 67 + vendor/tokio/tests/task_local.rs | 31 + vendor/tokio/tests/task_local_set.rs | 508 ++ vendor/tokio/tests/tcp_accept.rs | 157 + vendor/tokio/tests/tcp_connect.rs | 229 + vendor/tokio/tests/tcp_echo.rs | 42 + vendor/tokio/tests/tcp_into_split.rs | 131 + vendor/tokio/tests/tcp_into_std.rs | 45 + vendor/tokio/tests/tcp_peek.rs | 29 + vendor/tokio/tests/tcp_shutdown.rs | 28 + vendor/tokio/tests/tcp_socket.rs | 60 + vendor/tokio/tests/tcp_split.rs | 42 + vendor/tokio/tests/tcp_stream.rs | 359 + vendor/tokio/tests/test_clock.rs | 50 + vendor/tokio/tests/time_interval.rs | 175 + vendor/tokio/tests/time_pause.rs | 326 + vendor/tokio/tests/time_rt.rs | 89 + vendor/tokio/tests/time_sleep.rs | 372 + vendor/tokio/tests/time_timeout.rs | 137 + vendor/tokio/tests/udp.rs | 442 ++ vendor/tokio/tests/uds_cred.rs | 26 + vendor/tokio/tests/uds_datagram.rs | 330 + vendor/tokio/tests/uds_split.rs | 43 + vendor/tokio/tests/uds_stream.rs | 411 + .../tracing-attributes/.cargo-checksum.json | 2 +- vendor/tracing-attributes/CHANGELOG.md | 24 + vendor/tracing-attributes/Cargo.toml | 3 +- vendor/tracing-attributes/README.md | 4 +- vendor/tracing-attributes/src/attr.rs | 380 + vendor/tracing-attributes/src/expand.rs | 744 ++ vendor/tracing-attributes/src/lib.rs | 1084 +-- vendor/tracing-attributes/tests/async_fn.rs | 63 + vendor/tracing-attributes/tests/err.rs | 50 + vendor/tracing-attributes/tests/ret.rs | 226 + vendor/tracing-core/.cargo-checksum.json | 2 +- vendor/tracing-core/CHANGELOG.md | 37 + vendor/tracing-core/Cargo.toml | 12 +- vendor/tracing-core/README.md | 26 +- vendor/tracing-core/src/callsite.rs | 2 +- vendor/tracing-core/src/dispatcher.rs | 4 +- vendor/tracing-core/src/event.rs | 2 +- vendor/tracing-core/src/field.rs | 129 +- vendor/tracing-core/src/lib.rs | 55 +- vendor/tracing-core/src/metadata.rs | 88 +- vendor/tracing-core/src/span.rs | 2 +- vendor/tracing-core/src/subscriber.rs | 11 +- .../tracing-subscriber/.cargo-checksum.json | 2 +- vendor/tracing-subscriber/CHANGELOG.md | 141 + vendor/tracing-subscriber/Cargo.toml | 21 +- vendor/tracing-subscriber/README.md | 2 +- vendor/tracing-subscriber/src/field/mod.rs | 2 +- .../src/filter/directive.rs | 34 +- .../tracing-subscriber/src/filter/env/mod.rs | 1 + .../src/filter/layer_filters/combinator.rs | 380 + .../mod.rs} | 281 +- .../tracing-subscriber/src/filter/targets.rs | 31 +- .../tracing-subscriber/src/fmt/fmt_layer.rs | 127 +- .../tracing-subscriber/src/fmt/format/json.rs | 121 + .../tracing-subscriber/src/fmt/format/mod.rs | 325 +- .../src/fmt/format/pretty.rs | 78 +- vendor/tracing-subscriber/src/fmt/mod.rs | 182 +- vendor/tracing-subscriber/src/fmt/time/mod.rs | 6 +- .../src/fmt/time/time_crate.rs | 198 +- vendor/tracing-subscriber/src/fmt/writer.rs | 2 + vendor/tracing-subscriber/src/layer/mod.rs | 2 +- vendor/tracing-subscriber/src/lib.rs | 40 +- .../tests/layer_filters/combinators.rs | 42 + vendor/tracing/.cargo-checksum.json | 2 +- vendor/tracing/CHANGELOG.md | 49 +- vendor/tracing/Cargo.toml | 11 +- vendor/tracing/README.md | 31 +- vendor/tracing/benches/global_subscriber.rs | 136 + vendor/tracing/benches/subscriber.rs | 3 - vendor/tracing/src/field.rs | 114 +- vendor/tracing/src/instrument.rs | 2 +- vendor/tracing/src/level_filters.rs | 2 +- vendor/tracing/src/lib.rs | 133 +- vendor/tracing/src/macros.rs | 363 +- vendor/tracing/src/span.rs | 29 +- vendor/tracing/tests/enabled.rs | 26 + vendor/tracing/tests/event.rs | 27 +- .../filter_caching_is_lexically_scoped.rs | 4 +- ...s_are_not_reevaluated_for_the_same_span.rs | 5 +- ...re_reevaluated_for_different_call_sites.rs | 4 +- vendor/tracing/tests/macros.rs | 21 +- vendor/tracing/tests/span.rs | 111 +- vendor/tracing/tests/subscriber.rs | 13 +- .../unicode-segmentation/.cargo-checksum.json | 2 +- vendor/unicode-segmentation/Cargo.toml | 11 +- vendor/unicode-segmentation/README.md | 2 +- .../unicode-segmentation/benches/graphemes.rs | 18 +- .../benches/unicode_words.rs | 2 +- .../benches/word_bounds.rs | 2 +- .../unicode-segmentation/scripts/unicode.py | 2 +- .../scripts/unicode_gen_breaktests.py | 0 vendor/unicode-segmentation/src/grapheme.rs | 146 +- vendor/unicode-segmentation/src/lib.rs | 18 +- vendor/unicode-segmentation/src/sentence.rs | 164 +- vendor/unicode-segmentation/src/tables.rs | 1947 ++--- vendor/unicode-segmentation/src/test.rs | 142 +- vendor/unicode-segmentation/src/testdata.rs | 6682 ++++++++++++----- vendor/unicode-segmentation/src/word.rs | 295 +- vendor/valuable/.cargo-checksum.json | 1 + vendor/valuable/Cargo.lock | 617 ++ vendor/valuable/Cargo.toml | 41 + vendor/valuable/benches/structable.rs | 128 + vendor/valuable/build.rs | 39 + vendor/valuable/examples/derive.rs | 26 + vendor/valuable/examples/hello_world.rs | 68 + vendor/valuable/examples/print.rs | 106 + vendor/valuable/no_atomic.rs | 63 + vendor/valuable/src/enumerable.rs | 682 ++ vendor/valuable/src/field.rs | 166 + vendor/valuable/src/lib.rs | 146 + vendor/valuable/src/listable.rs | 257 + vendor/valuable/src/mappable.rs | 191 + vendor/valuable/src/named_values.rs | 222 + vendor/valuable/src/slice.rs | 428 ++ vendor/valuable/src/structable.rs | 496 ++ vendor/valuable/src/tuplable.rs | 339 + vendor/valuable/src/valuable.rs | 339 + vendor/valuable/src/value.rs | 877 +++ vendor/valuable/src/visit.rs | 459 ++ vendor/version_check/.cargo-checksum.json | 2 +- vendor/version_check/Cargo.toml | 12 +- vendor/version_check/README.md | 14 +- vendor/version_check/src/channel.rs | 7 +- vendor/version_check/src/date.rs | 66 +- vendor/version_check/src/lib.rs | 207 +- vendor/version_check/src/version.rs | 10 +- .../.cargo-checksum.json | 1 + .../CODE_OF_CONDUCT.md | 49 + .../CONTRIBUTING.md | 8 + .../Cargo.toml | 43 + .../LICENSE-APACHE | 0 .../LICENSE-Apache-2.0_WITH_LLVM-exception | 220 + .../LICENSE-MIT | 23 + .../ORG_CODE_OF_CONDUCT.md | 143 + .../README.md | 76 + .../SECURITY.md | 29 + .../old-bitflags.patch | 0 .../src/error.rs | 0 .../src/lib.rs | 36 + .../src/lib_generated.rs | 1853 +++++ vendor/wasi/.cargo-checksum.json | 2 +- vendor/wasi/Cargo.toml | 11 +- vendor/wasi/README.md | 18 + vendor/wasi/src/lib.rs | 13 +- vendor/wasi/src/lib_generated.rs | 2251 +++--- version | 2 +- 5094 files changed, 307700 insertions(+), 102763 deletions(-) create mode 100644 compiler/rustc_ast_pretty/src/pp/convenience.rs create mode 100644 compiler/rustc_ast_pretty/src/pp/ring.rs create mode 100644 compiler/rustc_ast_pretty/src/pprust/state/delimited.rs create mode 100644 compiler/rustc_ast_pretty/src/pprust/state/expr.rs create mode 100644 compiler/rustc_ast_pretty/src/pprust/state/item.rs delete mode 100644 compiler/rustc_builtin_macros/src/llvm_asm.rs create mode 100644 compiler/rustc_const_eval/src/util/call_kind.rs create mode 100644 compiler/rustc_data_structures/src/fingerprint/tests.rs create mode 100644 compiler/rustc_data_structures/src/intern.rs create mode 100644 compiler/rustc_data_structures/src/intern/tests.rs delete mode 100644 compiler/rustc_data_structures/src/ptr_key.rs create mode 100644 compiler/rustc_error_codes/src/error_codes/E0772.md create mode 100644 compiler/rustc_error_codes/src/error_codes/E0787.md create mode 100644 compiler/rustc_lint/src/pass_by_value.rs delete mode 100644 compiler/rustc_middle/src/hir/exports.rs create mode 100644 compiler/rustc_middle/src/hir/nested_filter.rs create mode 100644 compiler/rustc_middle/src/metadata.rs delete mode 100644 compiler/rustc_query_impl/src/stats.rs create mode 100644 compiler/rustc_resolve/src/access_levels.rs create mode 100644 compiler/rustc_target/src/asm/msp430.rs create mode 100644 compiler/rustc_target/src/spec/aarch64_unknown_none_hermitkernel.rs create mode 100644 compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabi.rs create mode 100644 compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs create mode 100644 compiler/rustc_ty_utils/src/assoc.rs create mode 100644 compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs create mode 100644 compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs create mode 100644 compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs create mode 100644 compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs create mode 100644 compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs create mode 100644 library/core/benches/str/char_count.rs create mode 100644 library/core/benches/str/corpora.rs rename library/core/src/{stream/stream.rs => async_iter/async_iter.rs} (61%) rename library/core/src/{stream => async_iter}/from_iter.rs (54%) rename library/core/src/{stream => async_iter}/mod.rs (56%) create mode 100644 library/core/src/str/count.rs create mode 100644 library/core/tests/pin_macro.rs create mode 100644 library/core/tests/waker.rs create mode 100644 library/portable-simd/crates/core_simd/tests/cast.rs create mode 100644 library/portable-simd/crates/std_float/Cargo.toml create mode 100644 library/portable-simd/crates/std_float/src/lib.rs create mode 100644 library/std/src/io/error/repr_bitpacked.rs create mode 100644 library/std/src/io/error/repr_unpacked.rs create mode 100644 library/std/src/thread/scoped.rs delete mode 100644 library/std/src/time/monotonic.rs delete mode 100644 library/stdarch/crates/core_arch/avx512vbmi2.md create mode 100644 library/stdarch/crates/std_detect/src/detect/arch/mod.rs delete mode 100644 library/stdarch/crates/std_detect/src/detect/error_macros.rs delete mode 100644 src/doc/book/listings/ch09-error-handling/no-listing-03-closures/Cargo.toml delete mode 100644 src/doc/book/listings/ch09-error-handling/no-listing-03-closures/src/main.rs rename src/doc/book/listings/ch14-more-about-cargo/{no-listing-04-workspace-with-tests/add/add-one => listing-14-07/add/add_one}/Cargo.toml (78%) rename src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/{add-one => add_one}/src/lib.rs (100%) delete mode 100644 src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add-one/Cargo.toml rename src/doc/book/listings/{ch17-oop/no-listing-01-trait-object-of-clone => ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add_one}/Cargo.toml (78%) rename src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/{add-one => add_one}/src/lib.rs (100%) rename src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/{add-one => add_one}/Cargo.toml (81%) rename src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/{add-one => add_one}/src/lib.rs (100%) rename src/doc/book/listings/ch14-more-about-cargo/{listing-14-07/add/add-one => no-listing-04-workspace-with-tests/add/add_one}/Cargo.toml (78%) rename src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/{add-one => add_one}/src/lib.rs (100%) delete mode 100644 src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/add-one/src/lib.rs rename src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/{add-one => add_one}/Cargo.toml (81%) rename src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/{add-one => add_one}/src/lib.rs (100%) delete mode 100644 src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/Cargo.lock delete mode 100644 src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/output.txt delete mode 100644 src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/src/lib.rs create mode 100644 src/doc/book/nostarch/chapter14.md create mode 100644 src/doc/book/nostarch/chapter15.md create mode 100644 src/doc/book/nostarch/chapter16.md create mode 100644 src/doc/book/nostarch/chapter17.md create mode 100644 src/doc/book/nostarch/chapter18.md create mode 100644 src/doc/reference/src/expressions/underscore-expr.md create mode 100644 src/doc/rustc-dev-guide/src/asm.md delete mode 100644 src/doc/rustc-dev-guide/src/compiletest.md create mode 100644 src/doc/rustc-dev-guide/src/tests/ci.md create mode 100644 src/doc/rustc-dev-guide/src/tests/compiletest.md create mode 100644 src/doc/rustc-dev-guide/src/tests/crater.md create mode 100644 src/doc/rustc-dev-guide/src/tests/docker.md create mode 100644 src/doc/rustc-dev-guide/src/tests/headers.md create mode 100644 src/doc/rustc-dev-guide/src/tests/perf.md create mode 100644 src/doc/rustc-dev-guide/src/tests/ui.md rename src/doc/{unstable-book/src/compiler-flags => rustc/src}/instrument-coverage.md (80%) create mode 100644 src/doc/rustc/src/platform-support/TEMPLATE.md create mode 100644 src/doc/rustc/src/platform-support/aarch64-unknown-none-hermitkernel.md create mode 100644 src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md create mode 100644 src/doc/rustc/src/platform-support/mips64-openwrt-linux-musl.md create mode 100644 src/doc/rustc/src/platform-support/openbsd.md create mode 100644 src/doc/unstable-book/src/compiler-flags/branch-protection.md create mode 100644 src/doc/unstable-book/src/compiler-flags/cf-protection.md delete mode 100644 src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md delete mode 100644 src/doc/unstable-book/src/language-features/cfg-panic.md delete mode 100644 src/doc/unstable-book/src/library-features/llvm-asm.md rename src/etc/{pre-commit.sh => pre-push.sh} (91%) create mode 100644 src/librustdoc/askama.toml create mode 100644 src/librustdoc/clean/render_macro_matchers.rs delete mode 100644 src/librustdoc/html/render/templates.rs delete mode 100644 src/librustdoc/html/static/images/rust-logo.png create mode 100644 src/librustdoc/html/static/images/rust-logo.svg create mode 100644 src/test/assembly/aarch64-pointer-auth.rs create mode 100644 src/test/assembly/asm/msp430-types.rs create mode 100644 src/test/codegen/branch-protection.rs create mode 100644 src/test/codegen/cf-protection.rs create mode 100644 src/test/codegen/debuginfo-generic-closure-env-names.rs delete mode 100644 src/test/codegen/no-output-asm-is-volatile.rs create mode 100644 src/test/codegen/sanitizer_memtag_attr_check.rs create mode 100644 src/test/codegen/unwind-abis/aapcs-unwind-abi.rs create mode 100644 src/test/codegen/unwind-abis/cdecl-unwind-abi.rs create mode 100644 src/test/codegen/unwind-abis/fastcall-unwind-abi.rs create mode 100644 src/test/codegen/unwind-abis/sysv64-unwind-abi.rs create mode 100644 src/test/codegen/unwind-abis/vectorcall-unwind-abi.rs create mode 100644 src/test/codegen/unwind-abis/win64-unwind-abi.rs create mode 100644 src/test/codegen/used_with_arg.rs create mode 100644 src/test/incremental/issue-92987-provisional-dep-node.rs create mode 100644 src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff create mode 100644 src/test/mir-opt/const_prop/invalid_constant.rs create mode 100644 src/test/mir-opt/early_otherwise_branch.opt3.EarlyOtherwiseBranch.diff delete mode 100644 src/test/mir-opt/early_otherwise_branch_noopt.noopt2.EarlyOtherwiseBranch.diff create mode 100644 src/test/mir-opt/early_otherwise_branch_soundness.no_deref_ptr.EarlyOtherwiseBranch.diff create mode 100644 src/test/mir-opt/early_otherwise_branch_soundness.no_downcast.EarlyOtherwiseBranch.diff create mode 100644 src/test/mir-opt/early_otherwise_branch_soundness.rs delete mode 100644 src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff delete mode 100644 src/test/mir-opt/unreachable_asm.rs delete mode 100644 src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff delete mode 100644 src/test/mir-opt/unreachable_asm_2.rs delete mode 100644 src/test/pretty/llvm-asm-clobbers.rs delete mode 100644 src/test/pretty/llvm-asm-options.rs create mode 100644 src/test/pretty/use-tree.rs create mode 100644 src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-93054.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.unused_mod.txt create mode 100644 src/test/run-make-fulldeps/coverage/issue-93054.rs create mode 100644 src/test/run-make-fulldeps/coverage/lib/unused_mod_helper.rs create mode 100644 src/test/run-make-fulldeps/coverage/unused_mod.rs delete mode 100644 src/test/run-make-fulldeps/libs-and-bins/Makefile delete mode 100644 src/test/run-make-fulldeps/libs-and-bins/foo.rs create mode 100644 src/test/run-make-fulldeps/pointer-auth-link-with-c/Makefile create mode 100644 src/test/run-make-fulldeps/pointer-auth-link-with-c/test.c create mode 100644 src/test/run-make-fulldeps/pointer-auth-link-with-c/test.rs create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/Makefile create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c create mode 100644 src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs create mode 100644 src/test/run-make/rustdoc-scrape-examples-test/Makefile create mode 100644 src/test/run-make/rustdoc-scrape-examples-test/examples/ex.rs create mode 100644 src/test/run-make/rustdoc-scrape-examples-test/src/lib.rs create mode 100644 src/test/run-make/rustdoc-scrape-examples-whitespace/Makefile create mode 100644 src/test/run-make/rustdoc-scrape-examples-whitespace/examples/ex.rs create mode 100644 src/test/run-make/rustdoc-scrape-examples-whitespace/src/lib.rs create mode 100755 src/test/run-make/static-pie/check_clang_version.sh create mode 100755 src/test/run-make/static-pie/check_gcc_version.sh create mode 100644 src/test/rustdoc-gui/mobile.goml rename src/{doc/book/listings/ch09-error-handling/no-listing-03-closures => test/rustdoc-gui/src/staged_api}/Cargo.lock (79%) create mode 100644 src/test/rustdoc-gui/src/staged_api/Cargo.toml create mode 100644 src/test/rustdoc-gui/src/staged_api/lib.rs create mode 100644 src/test/rustdoc-gui/theme-in-history.goml delete mode 100644 src/test/rustdoc-js-std/multi-query.js create mode 100644 src/test/rustdoc-js/generics-multi-trait.js create mode 100644 src/test/rustdoc-js/generics-multi-trait.rs create mode 100644 src/test/rustdoc-json/impls/blanket_with_local.rs create mode 100644 src/test/rustdoc-json/type/dyn.rs create mode 100644 src/test/rustdoc-json/type/fn_lifetime.rs create mode 100644 src/test/rustdoc-json/type/generic_default.rs create mode 100644 src/test/rustdoc-ui/block-doc-comment.rs create mode 100644 src/test/rustdoc-ui/block-doc-comment.stdout create mode 100644 src/test/rustdoc-ui/intra-doc/macro-rules.rs create mode 100644 src/test/rustdoc-ui/suggestions/html-as-generics-no-suggestions.rs create mode 100644 src/test/rustdoc-ui/suggestions/html-as-generics-no-suggestions.stderr create mode 100644 src/test/rustdoc-ui/suggestions/html-as-generics.fixed create mode 100644 src/test/rustdoc-ui/suggestions/html-as-generics.rs create mode 100644 src/test/rustdoc-ui/suggestions/html-as-generics.stderr create mode 100644 src/test/rustdoc/intra-doc/auxiliary/extern-inherent-impl-dep.rs create mode 100644 src/test/rustdoc/intra-doc/auxiliary/extern-lang-item-impl-dep.rs create mode 100644 src/test/rustdoc/intra-doc/crate-relative.rs create mode 100644 src/test/rustdoc/intra-doc/extern-inherent-impl.rs create mode 100644 src/test/rustdoc/intra-doc/extern-lang-item-impl.rs create mode 100644 src/test/rustdoc/intra-doc/mod-relative.rs rename src/test/rustdoc/{intra-link-prim-self.rs => intra-doc/prim-self.rs} (67%) rename src/test/rustdoc/{intra-link-self-cache.rs => intra-doc/self-cache.rs} (100%) create mode 100644 src/test/rustdoc/lifetime-name.rs create mode 100644 src/test/rustdoc/macro-generated-macro.macro_linebreak_pre.html create mode 100644 src/test/rustdoc/macro-generated-macro.macro_morestuff_pre.html create mode 100644 src/test/rustdoc/macro-generated-macro.rs create mode 100644 src/test/rustdoc/mixing-doc-comments-and-attrs.S3_top-doc.html create mode 100644 src/test/rustdoc/source-version-separator.rs create mode 100644 src/test/rustdoc/strip-block-doc-comments-stars.docblock.html create mode 100644 src/test/rustdoc/strip-block-doc-comments-stars.rs create mode 100644 src/test/rustdoc/version-separator-without-source.rs delete mode 100644 src/test/ui-fulldeps/internal-lints/pass_ty_by_ref.stderr delete mode 100644 src/test/ui-fulldeps/internal-lints/pass_ty_by_ref_self.stderr create mode 100644 src/test/ui-fulldeps/internal-lints/query_stability.rs create mode 100644 src/test/ui-fulldeps/internal-lints/query_stability.stderr create mode 100644 src/test/ui-fulldeps/internal-lints/query_stability_incorrect.rs create mode 100644 src/test/ui-fulldeps/internal-lints/query_stability_incorrect.stderr rename src/test/ui-fulldeps/internal-lints/{pass_ty_by_ref.rs => rustc_pass_by_value.rs} (55%) create mode 100644 src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.stderr rename src/test/ui-fulldeps/internal-lints/{pass_ty_by_ref_self.rs => rustc_pass_by_value_self.rs} (56%) create mode 100644 src/test/ui-fulldeps/internal-lints/rustc_pass_by_value_self.stderr create mode 100644 src/test/ui/aligned_enum_cast.rs create mode 100644 src/test/ui/asm/reg-conflict.rs create mode 100644 src/test/ui/asm/reg-conflict.stderr create mode 100644 src/test/ui/associated-consts/assoc-const-eq-missing.rs create mode 100644 src/test/ui/associated-consts/assoc-const-eq-missing.stderr create mode 100644 src/test/ui/associated-consts/assoc-const-ty-mismatch.rs create mode 100644 src/test/ui/associated-consts/assoc-const-ty-mismatch.stderr create mode 100644 src/test/ui/associated-consts/assoc-const.rs create mode 100644 src/test/ui/associated-consts/issue-93835.rs create mode 100644 src/test/ui/associated-consts/issue-93835.stderr create mode 100644 src/test/ui/associated-consts/shadowed-const.rs create mode 100644 src/test/ui/associated-consts/shadowed-const.stderr create mode 100644 src/test/ui/associated-types/issue-91069.rs create mode 100644 src/test/ui/async-await/interior-with-const-generic-expr.rs create mode 100644 src/test/ui/async-await/issue-93197.rs create mode 100644 src/test/ui/async-await/issue-93648.rs create mode 100644 src/test/ui/async-await/partial-drop-partial-reinit.rs create mode 100644 src/test/ui/async-await/partial-drop-partial-reinit.stderr create mode 100644 src/test/ui/async-await/proper-span-for-type-error.fixed create mode 100644 src/test/ui/async-await/proper-span-for-type-error.rs create mode 100644 src/test/ui/async-await/proper-span-for-type-error.stderr create mode 100644 src/test/ui/attributes/used_with_arg.rs create mode 100644 src/test/ui/attributes/used_with_arg.stderr create mode 100644 src/test/ui/attributes/used_with_multi_args.rs create mode 100644 src/test/ui/attributes/used_with_multi_args.stderr create mode 100644 src/test/ui/auto-traits/suspicious-impls-lint.rs create mode 100644 src/test/ui/auto-traits/suspicious-impls-lint.stderr create mode 100644 src/test/ui/autoref-autoderef/deref-into-array.rs delete mode 100644 src/test/ui/borrowck/borrowck-asm.rs delete mode 100644 src/test/ui/borrowck/borrowck-asm.stderr create mode 100644 src/test/ui/borrowck/issue-93093.rs create mode 100644 src/test/ui/borrowck/issue-93093.stderr create mode 100644 src/test/ui/box/issue-78459-ice.rs create mode 100644 src/test/ui/chalkify/assert.rs create mode 100644 src/test/ui/check-cfg/empty-names.rs create mode 100644 src/test/ui/check-cfg/empty-names.stderr create mode 100644 src/test/ui/check-cfg/empty-values.rs create mode 100644 src/test/ui/check-cfg/empty-values.stderr create mode 100644 src/test/ui/check-cfg/invalid-arguments.anything_else.stderr create mode 100644 src/test/ui/check-cfg/invalid-arguments.names_simple_ident.stderr create mode 100644 src/test/ui/check-cfg/invalid-arguments.rs create mode 100644 src/test/ui/check-cfg/invalid-arguments.values_simple_ident.stderr create mode 100644 src/test/ui/check-cfg/invalid-arguments.values_string_literals.stderr create mode 100644 src/test/ui/check-cfg/invalid-cfg-name.rs create mode 100644 src/test/ui/check-cfg/invalid-cfg-name.stderr create mode 100644 src/test/ui/check-cfg/invalid-cfg-value.rs create mode 100644 src/test/ui/check-cfg/invalid-cfg-value.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr create mode 100644 src/test/ui/closures/issue-84044-drop-non-mut.rs create mode 100644 src/test/ui/closures/issue-84044-drop-non-mut.stderr create mode 100644 src/test/ui/coherence/auxiliary/option_future.rs create mode 100644 src/test/ui/coherence/coherence-negative-outlives-lifetimes.rs create mode 100644 src/test/ui/coherence/coherence-negative-outlives-lifetimes.stderr create mode 100644 src/test/ui/coherence/coherence-overlap-negative-trait2.rs create mode 100644 src/test/ui/coherence/coherence-overlap-with-regions.rs create mode 100644 src/test/ui/conditional-compilation/cfg-arg-invalid-9.rs create mode 100644 src/test/ui/conditional-compilation/cfg-arg-invalid-9.stderr create mode 100644 src/test/ui/const-generics/const-generic-default-wont-borrowck.rs create mode 100644 src/test/ui/const-generics/const-generic-default-wont-borrowck.stderr create mode 100644 src/test/ui/const-generics/deref-into-array-generic.rs delete mode 100644 src/test/ui/const-generics/generic_arg_infer/array-in-sig.rs delete mode 100644 src/test/ui/const-generics/generic_arg_infer/array-in-sig.stderr create mode 100644 src/test/ui/const-generics/generic_arg_infer/in-signature.rs create mode 100644 src/test/ui/const-generics/generic_arg_infer/in-signature.stderr create mode 100644 src/test/ui/const-generics/generic_arg_infer/infer_arg_and_const_arg.rs create mode 100644 src/test/ui/const-generics/issues/issue-92186.rs create mode 100644 src/test/ui/const-generics/min_const_generics/forbid-self-no-normalize.rs create mode 100644 src/test/ui/const-generics/min_const_generics/forbid-self-no-normalize.stderr create mode 100644 src/test/ui/consts/const-block-const-bound.rs create mode 100644 src/test/ui/consts/const-block-const-bound.stderr create mode 100644 src/test/ui/consts/const-eval/heap/alloc_intrinsic_zero_sized.rs create mode 100644 src/test/ui/consts/const-eval/heap/dealloc_intrinsic.rs create mode 100644 src/test/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs create mode 100644 src/test/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr create mode 100644 src/test/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.rs create mode 100644 src/test/ui/consts/const-eval/heap/dealloc_intrinsic_duplicate.stderr create mode 100644 src/test/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.rs create mode 100644 src/test/ui/consts/const-eval/heap/dealloc_intrinsic_incorrect_layout.stderr create mode 100644 src/test/ui/consts/const-eval/heap/dealloc_intrinsic_zero_sized.rs create mode 100644 src/test/ui/consts/const-eval/ub-nonnull.chalk.64bit.stderr create mode 100644 src/test/ui/consts/const-eval/ub-wide-ptr.chalk.64bit.stderr create mode 100644 src/test/ui/consts/drop_box.rs create mode 100644 src/test/ui/consts/drop_box.stderr create mode 100644 src/test/ui/consts/issue-91560.fixed create mode 100644 src/test/ui/consts/issue-91560.rs create mode 100644 src/test/ui/consts/issue-91560.stderr create mode 100644 src/test/ui/consts/recursive.rs create mode 100644 src/test/ui/consts/recursive.stderr create mode 100644 src/test/ui/derives/issue-91550.rs create mode 100644 src/test/ui/derives/issue-91550.stderr create mode 100644 src/test/ui/did_you_mean/issue-93210-ignore-doc-hidden.rs create mode 100644 src/test/ui/did_you_mean/issue-93210-ignore-doc-hidden.stderr delete mode 100644 src/test/ui/error-codes/E0660.rs delete mode 100644 src/test/ui/error-codes/E0660.stderr delete mode 100644 src/test/ui/error-codes/E0661.rs delete mode 100644 src/test/ui/error-codes/E0661.stderr delete mode 100644 src/test/ui/error-codes/E0662.rs delete mode 100644 src/test/ui/error-codes/E0662.stderr delete mode 100644 src/test/ui/error-codes/E0663.rs delete mode 100644 src/test/ui/error-codes/E0663.stderr delete mode 100644 src/test/ui/error-codes/E0664.rs delete mode 100644 src/test/ui/error-codes/E0664.stderr delete mode 100644 src/test/ui/expr/if/attrs/let-chains-attr.stderr delete mode 100644 src/test/ui/feature-gates/feature-gate-allow_fail.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-allow_fail.stderr delete mode 100644 src/test/ui/feature-gates/feature-gate-asm.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-asm.stderr delete mode 100644 src/test/ui/feature-gates/feature-gate-asm2.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-asm2.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-associated_const_equality.rs create mode 100644 src/test/ui/feature-gates/feature-gate-associated_const_equality.stderr delete mode 100644 src/test/ui/feature-gates/feature-gate-cfg-panic.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-cfg-panic.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-cfg-target-has-atomic-equal-alignment.rs create mode 100644 src/test/ui/feature-gates/feature-gate-cfg-target-has-atomic-equal-alignment.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-check-cfg.rs create mode 100644 src/test/ui/feature-gates/feature-gate-check-cfg.stderr delete mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-raw-dylib-windows-gnu.stderr rename src/test/ui/feature-gates/{feature-gate-raw-dylib-windows-msvc.rs => feature-gate-raw-dylib.rs} (71%) rename src/test/ui/feature-gates/{feature-gate-raw-dylib-windows-msvc.stderr => feature-gate-raw-dylib.stderr} (88%) create mode 100644 src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.rs create mode 100644 src/test/ui/feature-gates/feature-gate-unsafe_pin_internals.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-used_with_arg.rs create mode 100644 src/test/ui/feature-gates/feature-gate-used_with_arg.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-with_negative_coherence.rs create mode 100644 src/test/ui/feature-gates/feature-gate-with_negative_coherence.stderr create mode 100644 src/test/ui/fmt/format-args-capture-issue-94010.rs create mode 100644 src/test/ui/fmt/format-args-capture-issue-94010.stderr create mode 100644 src/test/ui/fmt/format-with-yield-point.rs create mode 100644 src/test/ui/foreign/issue-91370-foreign-fn-block-impl.rs create mode 100644 src/test/ui/foreign/issue-91370-foreign-fn-block-impl.stderr create mode 100644 src/test/ui/functions-closures/fn-help-with-err-generic-is-not-function.rs create mode 100644 src/test/ui/functions-closures/fn-help-with-err-generic-is-not-function.stderr create mode 100644 src/test/ui/functions-closures/fn-help-with-err.rs create mode 100644 src/test/ui/functions-closures/fn-help-with-err.stderr create mode 100644 src/test/ui/generator/drop-control-flow.rs create mode 100644 src/test/ui/generator/drop-yield-twice.rs create mode 100644 src/test/ui/generator/drop-yield-twice.stderr create mode 100644 src/test/ui/generator/issue-57478.rs create mode 100644 src/test/ui/generator/issue-93161.rs create mode 100644 src/test/ui/generator/partial-drop.rs create mode 100644 src/test/ui/generator/partial-drop.stderr create mode 100644 src/test/ui/generator/reinit-in-match-guard.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-80626.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-80626.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-86218.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-86218.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-87735.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-87735.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-87748.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-87748.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-87755.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-87755.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-87803.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-87803.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-88382.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-88382.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-88460.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-88460.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-88526.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-88526.stderr create mode 100644 src/test/ui/generic-associated-types/bugs/issue-89008.rs create mode 100644 src/test/ui/generic-associated-types/bugs/issue-89008.stderr create mode 100644 src/test/ui/generic-associated-types/elided-in-expr-position.rs create mode 100644 src/test/ui/generic-associated-types/elided-in-expr-position.stderr create mode 100644 src/test/ui/generic-associated-types/issue-89352.rs create mode 100644 src/test/ui/generic-associated-types/issue-91139.rs create mode 100644 src/test/ui/generic-associated-types/issue-91762.rs create mode 100644 src/test/ui/generic-associated-types/issue-91762.stderr create mode 100644 src/test/ui/generic-associated-types/issue-92033.rs create mode 100644 src/test/ui/generic-associated-types/issue-92033.stderr create mode 100644 src/test/ui/generic-associated-types/issue-92096.migrate.stderr create mode 100644 src/test/ui/generic-associated-types/issue-92096.rs create mode 100644 src/test/ui/generic-associated-types/issue-92280.rs create mode 100644 src/test/ui/generic-associated-types/issue-92954.rs create mode 100644 src/test/ui/generic-associated-types/issue-93141.rs create mode 100644 src/test/ui/generic-associated-types/issue-93340.rs create mode 100644 src/test/ui/generic-associated-types/issue-93874.rs create mode 100644 src/test/ui/implied-bounds/hrlt-implied-trait-bounds-guard.rs create mode 100644 src/test/ui/implied-bounds/hrlt-implied-trait-bounds-guard.stderr create mode 100644 src/test/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs create mode 100644 src/test/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGS.stderr create mode 100644 src/test/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADTARGET.stderr create mode 100644 src/test/ui/invalid-compile-flags/branch-protection-missing-pac-ret.rs delete mode 100644 src/test/ui/issues/issue-14772.rs delete mode 100644 src/test/ui/issues/issue-14772.stderr create mode 100644 src/test/ui/iterators/collect-into-slice.rs create mode 100644 src/test/ui/iterators/collect-into-slice.stderr create mode 100644 src/test/ui/lint/lint-pub-unreachable-for-nested-glob.rs delete mode 100644 src/test/ui/llvm-asm/asm-src-loc-codegen-units.rs delete mode 100644 src/test/ui/llvm-asm/asm-src-loc.rs delete mode 100644 src/test/ui/llvm-asm/inline-asm-bad-constraint.rs delete mode 100644 src/test/ui/llvm-asm/inline-asm-bad-constraint.stderr delete mode 100644 src/test/ui/llvm-asm/inline-asm-bad-operand.rs delete mode 100644 src/test/ui/llvm-asm/inline-asm-bad-operand.stderr delete mode 100644 src/test/ui/llvm-asm/issue-14936.rs delete mode 100644 src/test/ui/llvm-asm/issue-23458.rs delete mode 100644 src/test/ui/llvm-asm/issue-23458.stderr delete mode 100644 src/test/ui/llvm-asm/issue-33264.rs delete mode 100644 src/test/ui/llvm-asm/issue-37366.rs delete mode 100644 src/test/ui/llvm-asm/issue-37433.rs delete mode 100644 src/test/ui/llvm-asm/issue-37433.stderr delete mode 100644 src/test/ui/llvm-asm/issue-51431.rs delete mode 100644 src/test/ui/llvm-asm/issue-51431.stderr delete mode 100644 src/test/ui/llvm-asm/issue-53787-inline-assembler-macro.rs delete mode 100644 src/test/ui/llvm-asm/issue-53787-inline-assembler-macro.stderr delete mode 100644 src/test/ui/llvm-asm/issue-54067.rs delete mode 100644 src/test/ui/llvm-asm/issue-62046.rs delete mode 100644 src/test/ui/llvm-asm/issue-62046.stderr delete mode 100644 src/test/ui/llvm-asm/issue-69092.rs delete mode 100644 src/test/ui/llvm-asm/issue-69092.stderr delete mode 100644 src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-bad-clobber.stderr delete mode 100644 src/test/ui/llvm-asm/llvm-asm-concat-src.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.stderr delete mode 100644 src/test/ui/llvm-asm/llvm-asm-in-moved.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-indirect-memory.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-literal-escaping.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-misplaced-option.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-misplaced-option.stderr delete mode 100644 src/test/ui/llvm-asm/llvm-asm-out-assign-imm.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr delete mode 100644 src/test/ui/llvm-asm/llvm-asm-out-assign.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-out-no-modifier.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-out-no-modifier.stderr delete mode 100644 src/test/ui/llvm-asm/llvm-asm-out-read-uninit.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-out-read-uninit.stderr delete mode 100644 src/test/ui/llvm-asm/llvm-asm-parse-errors.rs delete mode 100644 src/test/ui/llvm-asm/llvm-asm-parse-errors.stderr create mode 100644 src/test/ui/mir/mir_let_chains_drop_order.rs create mode 100644 src/test/ui/nll/lint-no-err.rs create mode 100644 src/test/ui/nll/lint-no-err.stderr create mode 100644 src/test/ui/numeric/uppercase-base-prefix.fixed create mode 100644 src/test/ui/numeric/uppercase-base-prefix.rs create mode 100644 src/test/ui/numeric/uppercase-base-prefix.stderr create mode 100644 src/test/ui/panics/default-backtrace-ice.rs create mode 100644 src/test/ui/panics/default-backtrace-ice.stderr create mode 100644 src/test/ui/panics/panic-handler-chain-update-hook.rs create mode 100644 src/test/ui/panics/runtime-switch.legacy.run.stderr create mode 100644 src/test/ui/panics/runtime-switch.rs create mode 100644 src/test/ui/panics/runtime-switch.v0.run.stderr create mode 100644 src/test/ui/parser/bad-escape-suggest-raw-string.rs create mode 100644 src/test/ui/parser/bad-escape-suggest-raw-string.stderr create mode 100644 src/test/ui/parser/bad-struct-following-where.rs create mode 100644 src/test/ui/parser/bad-struct-following-where.stderr create mode 100644 src/test/ui/parser/duplicate-where-clauses.rs create mode 100644 src/test/ui/parser/duplicate-where-clauses.stderr create mode 100644 src/test/ui/parser/issues/issue-93282.rs create mode 100644 src/test/ui/parser/issues/issue-93282.stderr create mode 100644 src/test/ui/parser/trailing-question-in-macro-type.rs create mode 100644 src/test/ui/parser/trailing-question-in-macro-type.stderr create mode 100644 src/test/ui/parser/trailing-question-in-type.fixed create mode 100644 src/test/ui/parser/trailing-question-in-type.rs create mode 100644 src/test/ui/parser/trailing-question-in-type.stderr delete mode 100644 src/test/ui/pattern/issue-82290.rs delete mode 100644 src/test/ui/pattern/issue-82290.stderr create mode 100644 src/test/ui/pin-macro/cant_access_internals.rs create mode 100644 src/test/ui/pin-macro/cant_access_internals.stderr create mode 100644 src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs create mode 100644 src/test/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr create mode 100644 src/test/ui/privacy/auxiliary/issue-92755.rs create mode 100644 src/test/ui/privacy/issue-92755.rs create mode 100644 src/test/ui/ptr_ops/issue-80309-safe.rs create mode 100644 src/test/ui/ptr_ops/issue-80309.rs create mode 100644 src/test/ui/resolve/resolve-hint-macro.fixed create mode 100644 src/test/ui/return/return-impl-trait-bad.rs create mode 100644 src/test/ui/return/return-impl-trait-bad.stderr create mode 100644 src/test/ui/return/return-impl-trait.fixed create mode 100644 src/test/ui/return/return-impl-trait.rs create mode 100644 src/test/ui/return/return-impl-trait.stderr create mode 100644 src/test/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/chains-without-let.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/chains-without-let.stderr create mode 100644 src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-88498.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-90722.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-92145.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-93150.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-93150.stderr create mode 100644 src/test/ui/rfc-2497-if-let-chains/no-double-assigments.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/then-else-blocks.rs delete mode 100644 src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.rs delete mode 100644 src/test/ui/rfc-2627-raw-dylib/raw-dylib-msvc-only.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr create mode 100644 src/test/ui/save-analysis/issue-89066.rs create mode 100644 src/test/ui/save-analysis/issue-89066.stderr create mode 100644 src/test/ui/simd/intrinsic/generic-as.rs create mode 100644 src/test/ui/simd/intrinsic/generic-cast-pointer-width.rs create mode 100644 src/test/ui/simd/libm_std_can_float.rs rename src/test/ui/specialization/{deafult-associated-type-bound-1.rs => default-associated-type-bound-1.rs} (95%) rename src/test/ui/specialization/{deafult-associated-type-bound-1.stderr => default-associated-type-bound-1.stderr} (78%) rename src/test/ui/specialization/{deafult-associated-type-bound-2.rs => default-associated-type-bound-2.rs} (100%) rename src/test/ui/specialization/{deafult-associated-type-bound-2.stderr => default-associated-type-bound-2.stderr} (88%) rename src/test/ui/specialization/{deafult-generic-associated-type-bound.rs => default-generic-associated-type-bound.rs} (100%) rename src/test/ui/specialization/{deafult-generic-associated-type-bound.stderr => default-generic-associated-type-bound.stderr} (86%) create mode 100644 src/test/ui/stability-attribute/generics-default-stability-trait.rs create mode 100644 src/test/ui/stability-attribute/generics-default-stability-trait.stderr create mode 100644 src/test/ui/suggestions/args-instead-of-tuple-errors.rs create mode 100644 src/test/ui/suggestions/args-instead-of-tuple-errors.stderr create mode 100644 src/test/ui/suggestions/args-instead-of-tuple.fixed create mode 100644 src/test/ui/suggestions/args-instead-of-tuple.rs create mode 100644 src/test/ui/suggestions/args-instead-of-tuple.stderr create mode 100644 src/test/ui/suggestions/private-field.rs create mode 100644 src/test/ui/suggestions/private-field.stderr create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-path-in-type.rs create mode 100644 src/test/ui/suggestions/type-ascription-instead-of-path-in-type.stderr create mode 100644 src/test/ui/symbol-names/foreign-types.rs create mode 100644 src/test/ui/symbol-names/foreign-types.stderr create mode 100644 src/test/ui/symbol-names/verbose.rs create mode 100644 src/test/ui/target-feature/tied-features-cli.one.stderr create mode 100644 src/test/ui/target-feature/tied-features-cli.rs create mode 100644 src/test/ui/target-feature/tied-features-cli.three.stderr create mode 100644 src/test/ui/target-feature/tied-features-cli.two.stderr create mode 100644 src/test/ui/target-feature/tied-features.rs create mode 100644 src/test/ui/target-feature/tied-features.stderr delete mode 100644 src/test/ui/test-attrs/test-allow-fail-attr.rs delete mode 100644 src/test/ui/test-attrs/test-on-macro.rs delete mode 100644 src/test/ui/test-attrs/test-on-macro.stderr create mode 100644 src/test/ui/test-attrs/test-on-not-fn.rs create mode 100644 src/test/ui/test-attrs/test-on-not-fn.stderr create mode 100644 src/test/ui/trait-bounds/issue-93008.rs create mode 100644 src/test/ui/trait-bounds/issue-93008.stderr create mode 100644 src/test/ui/traits/default-method/rustc_must_implement_one_of.rs create mode 100644 src/test/ui/traits/default-method/rustc_must_implement_one_of.stderr create mode 100644 src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs create mode 100644 src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.stderr create mode 100644 src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.rs create mode 100644 src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr create mode 100644 src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs create mode 100644 src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr create mode 100644 src/test/ui/traits/issue-91594.rs create mode 100644 src/test/ui/traits/issue-91594.stderr create mode 100644 src/test/ui/traits/pointee-deduction.rs create mode 100644 src/test/ui/tuple/array-diagnostics.rs create mode 100644 src/test/ui/tuple/array-diagnostics.stderr create mode 100644 src/test/ui/tuple/wrong_argument_ice-2.rs create mode 100644 src/test/ui/tuple/wrong_argument_ice-2.stderr create mode 100644 src/test/ui/tuple/wrong_argument_ice-3.rs create mode 100644 src/test/ui/tuple/wrong_argument_ice-3.stderr create mode 100644 src/test/ui/typeck/issue-91328.fixed create mode 100644 src/test/ui/typeck/issue-91328.rs create mode 100644 src/test/ui/typeck/issue-91328.stderr create mode 100644 src/test/ui/typeck/issue-93486.rs create mode 100644 src/test/ui/typeck/issue-93486.stderr create mode 100644 src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs create mode 100644 src/tools/clippy/clippy_lints/src/default_union_representation.rs create mode 100644 src/tools/clippy/clippy_lints/src/manual_bits.rs delete mode 100644 src/tools/clippy/clippy_lints/src/matches.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/infalliable_detructuring_match.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/match_bool.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/mod.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/single_match.rs create mode 100644 src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs create mode 100644 src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs create mode 100644 src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs create mode 100644 src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs create mode 100644 src/tools/clippy/clippy_utils/src/macros.rs delete mode 100644 src/tools/clippy/tests/cargo/mod.rs create mode 100644 src/tools/clippy/tests/test_utils/mod.rs rename src/{doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/add-one => tools/clippy/tests/ui-cargo/multiple_config_files/no_warn}/Cargo.toml (90%) create mode 100644 src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/clippy.toml create mode 100644 src/tools/clippy/tests/ui-cargo/multiple_config_files/no_warn/src/main.rs create mode 100644 src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/.clippy.toml create mode 100644 src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/Cargo.toml create mode 100644 src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/clippy.toml create mode 100644 src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.rs create mode 100644 src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr create mode 100644 src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr create mode 100644 src/tools/clippy/tests/ui/borrow_as_ptr.fixed create mode 100644 src/tools/clippy/tests/ui/borrow_as_ptr.rs create mode 100644 src/tools/clippy/tests/ui/borrow_as_ptr.stderr create mode 100644 src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed create mode 100644 src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs create mode 100644 src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr create mode 100644 src/tools/clippy/tests/ui/cmp_owned/comparison_flip.fixed create mode 100644 src/tools/clippy/tests/ui/cmp_owned/comparison_flip.rs create mode 100644 src/tools/clippy/tests/ui/cmp_owned/comparison_flip.stderr create mode 100644 src/tools/clippy/tests/ui/crashes/ice-8250.rs create mode 100644 src/tools/clippy/tests/ui/crashes/ice-8250.stderr create mode 100644 src/tools/clippy/tests/ui/crashes/ice-8386.rs create mode 100644 src/tools/clippy/tests/ui/default_union_representation.rs create mode 100644 src/tools/clippy/tests/ui/default_union_representation.stderr delete mode 100644 src/tools/clippy/tests/ui/explicit_write_non_rustfix.rs delete mode 100644 src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr create mode 100644 src/tools/clippy/tests/ui/iter_overeager_cloned.fixed create mode 100644 src/tools/clippy/tests/ui/iter_overeager_cloned.rs create mode 100644 src/tools/clippy/tests/ui/iter_overeager_cloned.stderr create mode 100644 src/tools/clippy/tests/ui/manual_bits.fixed create mode 100644 src/tools/clippy/tests/ui/manual_bits.rs create mode 100644 src/tools/clippy/tests/ui/manual_bits.stderr delete mode 100644 src/tools/clippy/tests/ui/non_expressive_names.stdout create mode 100644 src/tools/clippy/tests/ui/single_char_lifetime_names.rs create mode 100644 src/tools/clippy/tests/ui/single_char_lifetime_names.stderr create mode 100644 src/tools/clippy/tests/ui/transmute_undefined_repr.rs create mode 100644 src/tools/clippy/tests/ui/transmute_undefined_repr.stderr delete mode 100644 src/tools/clippy/tests/ui/unnecessary_ref.fixed delete mode 100644 src/tools/clippy/tests/ui/unnecessary_ref.rs delete mode 100644 src/tools/clippy/tests/ui/unnecessary_ref.stderr delete mode 100644 src/tools/clippy/tests/ui_test/eq_op.rs create mode 100644 src/tools/clippy/tests/workspace.rs rename src/tools/clippy/{clippy_workspace_tests => tests/workspace_test}/Cargo.toml (71%) rename src/tools/clippy/{clippy_workspace_tests => tests/workspace_test}/build.rs (100%) rename src/tools/clippy/{clippy_workspace_tests => tests/workspace_test}/path_dep/Cargo.toml (100%) rename src/tools/clippy/{clippy_workspace_tests => tests/workspace_test}/path_dep/src/lib.rs (100%) rename src/tools/clippy/{clippy_workspace_tests => tests/workspace_test}/src/main.rs (100%) rename src/tools/clippy/{clippy_workspace_tests => tests/workspace_test}/subcrate/Cargo.toml (100%) rename src/tools/clippy/{clippy_workspace_tests => tests/workspace_test}/subcrate/src/lib.rs (100%) create mode 100644 vendor/ahash/.cargo-checksum.json create mode 100644 vendor/ahash/Cargo.toml create mode 100644 vendor/ahash/FAQ.md rename vendor/{crossbeam-deque-0.7.4 => ahash}/LICENSE-APACHE (100%) rename vendor/{maybe-uninit => ahash}/LICENSE-MIT (95%) create mode 100644 vendor/ahash/README.md create mode 100644 vendor/ahash/build.rs create mode 100644 vendor/ahash/rustfmt.toml create mode 100644 vendor/ahash/src/aes_hash.rs create mode 100644 vendor/ahash/src/convert.rs create mode 100644 vendor/ahash/src/fallback_hash.rs create mode 100644 vendor/ahash/src/hash_map.rs create mode 100644 vendor/ahash/src/hash_quality_test.rs create mode 100644 vendor/ahash/src/hash_set.rs create mode 100644 vendor/ahash/src/lib.rs create mode 100644 vendor/ahash/src/operations.rs create mode 100644 vendor/ahash/src/random_state.rs create mode 100644 vendor/ahash/src/specialize.rs create mode 100644 vendor/ahash/tests/bench.rs create mode 100644 vendor/ahash/tests/map_tests.rs create mode 100644 vendor/ahash/tests/nopanic.rs create mode 100644 vendor/ammonia/SECURITY.md create mode 100644 vendor/ammonia/bors.toml delete mode 100644 vendor/ansi_term-0.11.0/.cargo-checksum.json delete mode 100644 vendor/ansi_term-0.11.0/Cargo.toml delete mode 100644 vendor/ansi_term-0.11.0/LICENCE delete mode 100644 vendor/ansi_term-0.11.0/README.md delete mode 100644 vendor/ansi_term-0.11.0/examples/colours.rs delete mode 100644 vendor/ansi_term-0.11.0/src/ansi.rs delete mode 100644 vendor/ansi_term-0.11.0/src/debug.rs delete mode 100644 vendor/ansi_term-0.11.0/src/difference.rs delete mode 100644 vendor/ansi_term-0.11.0/src/display.rs delete mode 100644 vendor/ansi_term-0.11.0/src/lib.rs delete mode 100644 vendor/ansi_term-0.11.0/src/style.rs delete mode 100644 vendor/ansi_term-0.11.0/src/windows.rs delete mode 100644 vendor/ansi_term-0.11.0/src/write.rs create mode 100644 vendor/askama/.cargo-checksum.json create mode 100644 vendor/askama/Cargo.toml rename vendor/{parking_lot => askama}/LICENSE-APACHE (99%) rename vendor/{opaque-debug-0.2.3 => askama}/LICENSE-MIT (94%) create mode 100644 vendor/askama/src/lib.rs create mode 100644 vendor/askama_derive/.cargo-checksum.json create mode 100644 vendor/askama_derive/Cargo.toml rename vendor/{parking_lot_core => askama_derive}/LICENSE-APACHE (99%) rename vendor/{semver-parser => askama_derive}/LICENSE-MIT (94%) create mode 100644 vendor/askama_derive/README.md create mode 100644 vendor/askama_derive/src/lib.rs create mode 100644 vendor/askama_escape/.cargo-checksum.json create mode 100644 vendor/askama_escape/Cargo.toml rename vendor/{semver-parser => askama_escape}/LICENSE-APACHE (97%) rename vendor/{semver-0.11.0 => askama_escape}/LICENSE-MIT (93%) create mode 100644 vendor/askama_escape/README.md create mode 100644 vendor/askama_escape/benches/all.rs create mode 100644 vendor/askama_escape/src/lib.rs create mode 100644 vendor/askama_shared/.cargo-checksum.json create mode 100644 vendor/askama_shared/Cargo.toml create mode 100644 vendor/askama_shared/LICENSE-APACHE create mode 100644 vendor/askama_shared/LICENSE-MIT create mode 100644 vendor/askama_shared/README.md create mode 100644 vendor/askama_shared/src/error.rs create mode 100644 vendor/askama_shared/src/filters/json.rs create mode 100644 vendor/askama_shared/src/filters/mod.rs create mode 100644 vendor/askama_shared/src/filters/yaml.rs create mode 100644 vendor/askama_shared/src/generator.rs create mode 100644 vendor/askama_shared/src/helpers/mod.rs create mode 100644 vendor/askama_shared/src/heritage.rs create mode 100644 vendor/askama_shared/src/input.rs create mode 100644 vendor/askama_shared/src/lib.rs create mode 100644 vendor/askama_shared/src/parser.rs create mode 100644 vendor/askama_shared/templates/a.html create mode 100644 vendor/askama_shared/templates/b.html create mode 100644 vendor/askama_shared/templates/sub/b.html create mode 100644 vendor/askama_shared/templates/sub/c.html create mode 100644 vendor/askama_shared/templates/sub/sub1/d.html create mode 100644 vendor/block-buffer/CHANGELOG.md create mode 100644 vendor/block-buffer/README.md create mode 100644 vendor/block-buffer/src/sealed.rs create mode 100644 vendor/block-buffer/tests/mod.rs create mode 100644 vendor/bytes/.cargo-checksum.json create mode 100644 vendor/bytes/CHANGELOG.md rename vendor/{cargo_metadata-0.12.0 => bytes}/Cargo.toml (51%) create mode 100644 vendor/bytes/LICENSE create mode 100644 vendor/bytes/README.md create mode 100644 vendor/bytes/benches/buf.rs create mode 100644 vendor/bytes/benches/bytes.rs create mode 100644 vendor/bytes/benches/bytes_mut.rs rename vendor/{memoffset-0.5.5 => bytes}/ci/miri.sh (62%) mode change 100644 => 100755 create mode 100644 vendor/bytes/ci/test-stable.sh create mode 100644 vendor/bytes/ci/tsan.sh create mode 100644 vendor/bytes/src/buf/buf_impl.rs create mode 100644 vendor/bytes/src/buf/buf_mut.rs create mode 100644 vendor/bytes/src/buf/chain.rs create mode 100644 vendor/bytes/src/buf/iter.rs create mode 100644 vendor/bytes/src/buf/limit.rs create mode 100644 vendor/bytes/src/buf/mod.rs create mode 100644 vendor/bytes/src/buf/reader.rs create mode 100644 vendor/bytes/src/buf/take.rs create mode 100644 vendor/bytes/src/buf/uninit_slice.rs create mode 100644 vendor/bytes/src/buf/vec_deque.rs create mode 100644 vendor/bytes/src/buf/writer.rs create mode 100644 vendor/bytes/src/bytes.rs create mode 100644 vendor/bytes/src/bytes_mut.rs create mode 100644 vendor/bytes/src/fmt/debug.rs create mode 100644 vendor/bytes/src/fmt/hex.rs create mode 100644 vendor/bytes/src/fmt/mod.rs create mode 100644 vendor/bytes/src/lib.rs create mode 100644 vendor/bytes/src/loom.rs create mode 100644 vendor/bytes/src/serde.rs create mode 100644 vendor/bytes/tests/test_buf.rs create mode 100644 vendor/bytes/tests/test_buf_mut.rs create mode 100644 vendor/bytes/tests/test_bytes.rs create mode 100644 vendor/bytes/tests/test_bytes_odd_alloc.rs create mode 100644 vendor/bytes/tests/test_bytes_vec_alloc.rs create mode 100644 vendor/bytes/tests/test_chain.rs create mode 100644 vendor/bytes/tests/test_debug.rs create mode 100644 vendor/bytes/tests/test_iter.rs create mode 100644 vendor/bytes/tests/test_reader.rs create mode 100644 vendor/bytes/tests/test_serde.rs create mode 100644 vendor/bytes/tests/test_take.rs delete mode 100644 vendor/camino/Cargo.lock delete mode 100644 vendor/camino/examples/serde.rs delete mode 100644 vendor/camino/examples/structopt.rs delete mode 100644 vendor/cargo_metadata-0.12.0/.cargo-checksum.json delete mode 100644 vendor/cargo_metadata-0.12.0/README.md delete mode 100644 vendor/cargo_metadata-0.12.0/src/dependency.rs delete mode 100644 vendor/cargo_metadata-0.12.0/src/diagnostic.rs delete mode 100644 vendor/cargo_metadata-0.12.0/src/errors.rs delete mode 100644 vendor/cargo_metadata-0.12.0/src/lib.rs delete mode 100644 vendor/cargo_metadata-0.12.0/src/messages.rs delete mode 100644 vendor/cargo_metadata-0.12.0/tests/selftest.rs delete mode 100644 vendor/cargo_metadata-0.12.0/tests/test_samples.rs rename vendor/{clap => clap-2.34.0}/.cargo-checksum.json (100%) rename vendor/{clap => clap-2.34.0}/CHANGELOG.md (100%) rename vendor/{clap => clap-2.34.0}/CONTRIBUTORS.md (100%) rename vendor/{clap => clap-2.34.0}/Cargo.toml (100%) rename vendor/{clap => clap-2.34.0}/LICENSE-MIT (100%) rename vendor/{clap => clap-2.34.0}/README.md (100%) rename vendor/{clap => clap-2.34.0}/SPONSORS.md (100%) rename vendor/{clap => clap-2.34.0}/clap-test.rs (100%) rename vendor/{clap => clap-2.34.0}/justfile (100%) rename vendor/{clap => clap-2.34.0}/src/app/help.rs (100%) rename vendor/{clap => clap-2.34.0}/src/app/meta.rs (100%) rename vendor/{clap => clap-2.34.0}/src/app/mod.rs (100%) rename vendor/{clap => clap-2.34.0}/src/app/parser.rs (100%) rename vendor/{clap => clap-2.34.0}/src/app/settings.rs (100%) rename vendor/{clap => clap-2.34.0}/src/app/usage.rs (100%) rename vendor/{clap => clap-2.34.0}/src/app/validator.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/any_arg.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_builder/base.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_builder/flag.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_builder/mod.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_builder/option.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_builder/positional.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_builder/switched.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_builder/valued.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_matcher.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/arg_matches.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/group.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/macros.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/matched_arg.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/mod.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/settings.rs (100%) rename vendor/{clap => clap-2.34.0}/src/args/subcommand.rs (100%) rename vendor/{clap => clap-2.34.0}/src/completions/bash.rs (100%) rename vendor/{clap => clap-2.34.0}/src/completions/elvish.rs (100%) rename vendor/{clap => clap-2.34.0}/src/completions/fish.rs (100%) rename vendor/{clap => clap-2.34.0}/src/completions/macros.rs (100%) rename vendor/{clap => clap-2.34.0}/src/completions/mod.rs (100%) rename vendor/{clap => clap-2.34.0}/src/completions/powershell.rs (100%) rename vendor/{clap => clap-2.34.0}/src/completions/shell.rs (100%) rename vendor/{clap => clap-2.34.0}/src/completions/zsh.rs (100%) rename vendor/{clap => clap-2.34.0}/src/errors.rs (100%) rename vendor/{clap => clap-2.34.0}/src/fmt.rs (100%) rename vendor/{clap => clap-2.34.0}/src/lib.rs (100%) rename vendor/{clap => clap-2.34.0}/src/macros.rs (100%) rename vendor/{clap => clap-2.34.0}/src/map.rs (100%) rename vendor/{clap => clap-2.34.0}/src/osstringext.rs (100%) rename vendor/{clap => clap-2.34.0}/src/strext.rs (100%) rename vendor/{clap => clap-2.34.0}/src/suggestions.rs (100%) rename vendor/{clap => clap-2.34.0}/src/usage_parser.rs (100%) create mode 100644 vendor/cpufeatures/.cargo-checksum.json create mode 100644 vendor/cpufeatures/CHANGELOG.md rename vendor/{cpuid-bool => cpufeatures}/Cargo.toml (64%) rename vendor/{cpuid-bool => cpufeatures}/LICENSE-APACHE (100%) rename vendor/{cpuid-bool => cpufeatures}/LICENSE-MIT (100%) create mode 100644 vendor/cpufeatures/README.md create mode 100644 vendor/cpufeatures/src/aarch64.rs create mode 100644 vendor/cpufeatures/src/lib.rs create mode 100644 vendor/cpufeatures/src/x86.rs create mode 100644 vendor/cpufeatures/tests/aarch64.rs create mode 100644 vendor/cpufeatures/tests/x86.rs delete mode 100644 vendor/cpuid-bool/.cargo-checksum.json delete mode 100644 vendor/cpuid-bool/CHANGELOG.md delete mode 100644 vendor/cpuid-bool/src/lib.rs delete mode 100644 vendor/crossbeam-deque-0.7.4/.cargo-checksum.json delete mode 100644 vendor/crossbeam-deque-0.7.4/CHANGELOG.md delete mode 100644 vendor/crossbeam-deque-0.7.4/Cargo.toml delete mode 100644 vendor/crossbeam-deque-0.7.4/LICENSE-MIT delete mode 100644 vendor/crossbeam-deque-0.7.4/README.md delete mode 100644 vendor/crossbeam-deque-0.7.4/src/lib.rs delete mode 100644 vendor/crossbeam-deque-0.7.4/tests/fifo.rs delete mode 100644 vendor/crossbeam-deque-0.7.4/tests/injector.rs delete mode 100644 vendor/crossbeam-deque-0.7.4/tests/lifo.rs delete mode 100644 vendor/crossbeam-deque-0.7.4/tests/steal.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/.cargo-checksum.json delete mode 100644 vendor/crossbeam-epoch-0.8.2/CHANGELOG.md delete mode 100644 vendor/crossbeam-epoch-0.8.2/Cargo.lock delete mode 100644 vendor/crossbeam-epoch-0.8.2/Cargo.toml delete mode 100644 vendor/crossbeam-epoch-0.8.2/LICENSE-MIT delete mode 100644 vendor/crossbeam-epoch-0.8.2/README.md delete mode 100644 vendor/crossbeam-epoch-0.8.2/benches/defer.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/benches/flush.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/benches/pin.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/build.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/examples/sanitize.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/examples/treiber_stack.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/atomic.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/collector.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/default.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/deferred.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/epoch.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/guard.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/internal.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/lib.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/sync/list.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/sync/mod.rs delete mode 100644 vendor/crossbeam-epoch-0.8.2/src/sync/queue.rs delete mode 100644 vendor/crossbeam-queue/.cargo-checksum.json delete mode 100644 vendor/crossbeam-queue/CHANGELOG.md delete mode 100644 vendor/crossbeam-queue/Cargo.toml delete mode 100644 vendor/crossbeam-queue/LICENSE-MIT delete mode 100644 vendor/crossbeam-queue/LICENSE-THIRD-PARTY delete mode 100644 vendor/crossbeam-queue/README.md delete mode 100644 vendor/crossbeam-queue/src/array_queue.rs delete mode 100644 vendor/crossbeam-queue/src/err.rs delete mode 100644 vendor/crossbeam-queue/src/lib.rs delete mode 100644 vendor/crossbeam-queue/src/seg_queue.rs delete mode 100644 vendor/crossbeam-queue/tests/array_queue.rs delete mode 100644 vendor/crossbeam-queue/tests/seg_queue.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/.cargo-checksum.json delete mode 100644 vendor/crossbeam-utils-0.7.2/CHANGELOG.md delete mode 100644 vendor/crossbeam-utils-0.7.2/Cargo.toml delete mode 100644 vendor/crossbeam-utils-0.7.2/LICENSE-MIT delete mode 100644 vendor/crossbeam-utils-0.7.2/README.md delete mode 100644 vendor/crossbeam-utils-0.7.2/benches/atomic_cell.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/build.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/atomic/atomic_cell.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/atomic/consume.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/atomic/mod.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/atomic/seq_lock.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/atomic/seq_lock_wide.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/backoff.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/cache_padded.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/lib.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/sync/mod.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/sync/parker.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/sync/sharded_lock.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/sync/wait_group.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/src/thread.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/tests/atomic_cell.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/tests/cache_padded.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/tests/parker.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/tests/sharded_lock.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/tests/thread.rs delete mode 100644 vendor/crossbeam-utils-0.7.2/tests/wait_group.rs create mode 100644 vendor/crypto-common/.cargo-checksum.json create mode 100644 vendor/crypto-common/CHANGELOG.md create mode 100644 vendor/crypto-common/Cargo.toml rename vendor/{opaque-debug-0.2.3 => crypto-common}/LICENSE-APACHE (100%) create mode 100644 vendor/crypto-common/LICENSE-MIT create mode 100644 vendor/crypto-common/README.md create mode 100644 vendor/crypto-common/src/lib.rs delete mode 100644 vendor/difference/.cargo-checksum.json delete mode 100644 vendor/difference/Cargo.toml delete mode 100644 vendor/difference/Examples.md delete mode 100644 vendor/difference/LICENSE delete mode 100644 vendor/difference/README.md delete mode 100644 vendor/difference/appveyor.yml delete mode 100644 vendor/difference/assets/fox.png delete mode 100644 vendor/difference/assets/git-style.png delete mode 100644 vendor/difference/assets/github-style.png delete mode 100644 vendor/difference/assets/word-underline.png delete mode 100644 vendor/difference/examples/github-style.rs delete mode 100644 vendor/difference/examples/line-by-line.rs delete mode 100644 vendor/difference/examples/underline-words.rs delete mode 100644 vendor/difference/rustfmt.toml delete mode 100644 vendor/difference/src/display.rs delete mode 100644 vendor/difference/src/lcs.rs delete mode 100644 vendor/difference/src/lib.rs delete mode 100644 vendor/difference/src/main.rs delete mode 100644 vendor/difference/src/merge.rs delete mode 100644 vendor/difference/tests/quickcheck.rs create mode 100644 vendor/digest/src/core_api.rs create mode 100644 vendor/digest/src/core_api/ct_variable.rs create mode 100644 vendor/digest/src/core_api/rt_variable.rs create mode 100644 vendor/digest/src/core_api/wrapper.rs create mode 100644 vendor/digest/src/core_api/xof_reader.rs create mode 100644 vendor/digest/src/dev/fixed.rs create mode 100644 vendor/digest/src/dev/mac.rs create mode 100644 vendor/digest/src/dev/rng.rs create mode 100644 vendor/digest/src/dev/variable.rs create mode 100644 vendor/digest/src/dev/xof.rs delete mode 100644 vendor/digest/src/dyn_digest.rs delete mode 100644 vendor/digest/src/errors.rs delete mode 100644 vendor/digest/src/fixed.rs create mode 100644 vendor/digest/src/mac.rs delete mode 100644 vendor/digest/src/variable.rs delete mode 100644 vendor/digest/src/xof.rs create mode 100644 vendor/futures-channel/.cargo-checksum.json create mode 100644 vendor/futures-channel/Cargo.toml rename vendor/{semver-0.11.0 => futures-channel}/LICENSE-APACHE (97%) create mode 100644 vendor/futures-channel/LICENSE-MIT create mode 100644 vendor/futures-channel/README.md create mode 100644 vendor/futures-channel/benches/sync_mpsc.rs create mode 100644 vendor/futures-channel/build.rs create mode 100644 vendor/futures-channel/no_atomic_cas.rs create mode 100644 vendor/futures-channel/src/lib.rs create mode 100644 vendor/futures-channel/src/lock.rs create mode 100644 vendor/futures-channel/src/mpsc/mod.rs create mode 100644 vendor/futures-channel/src/mpsc/queue.rs create mode 100644 vendor/futures-channel/src/mpsc/sink_impl.rs create mode 100644 vendor/futures-channel/src/oneshot.rs create mode 100644 vendor/futures-channel/tests/channel.rs create mode 100644 vendor/futures-channel/tests/mpsc-close.rs create mode 100644 vendor/futures-channel/tests/mpsc.rs create mode 100644 vendor/futures-channel/tests/oneshot.rs create mode 100644 vendor/futures-core/.cargo-checksum.json create mode 100644 vendor/futures-core/Cargo.toml create mode 100644 vendor/futures-core/LICENSE-APACHE create mode 100644 vendor/futures-core/LICENSE-MIT create mode 100644 vendor/futures-core/README.md create mode 100644 vendor/futures-core/build.rs create mode 100644 vendor/futures-core/no_atomic_cas.rs create mode 100644 vendor/futures-core/src/future.rs create mode 100644 vendor/futures-core/src/lib.rs create mode 100644 vendor/futures-core/src/stream.rs create mode 100644 vendor/futures-core/src/task/__internal/atomic_waker.rs create mode 100644 vendor/futures-core/src/task/__internal/mod.rs create mode 100644 vendor/futures-core/src/task/mod.rs create mode 100644 vendor/futures-core/src/task/poll.rs create mode 100644 vendor/futures-executor/.cargo-checksum.json create mode 100644 vendor/futures-executor/Cargo.toml create mode 100644 vendor/futures-executor/LICENSE-APACHE create mode 100644 vendor/futures-executor/LICENSE-MIT create mode 100644 vendor/futures-executor/README.md create mode 100644 vendor/futures-executor/benches/thread_notify.rs create mode 100644 vendor/futures-executor/src/enter.rs create mode 100644 vendor/futures-executor/src/lib.rs create mode 100644 vendor/futures-executor/src/local_pool.rs create mode 100644 vendor/futures-executor/src/thread_pool.rs create mode 100644 vendor/futures-executor/src/unpark_mutex.rs create mode 100644 vendor/futures-executor/tests/local_pool.rs create mode 100644 vendor/futures-io/.cargo-checksum.json create mode 100644 vendor/futures-io/Cargo.toml create mode 100644 vendor/futures-io/LICENSE-APACHE create mode 100644 vendor/futures-io/LICENSE-MIT create mode 100644 vendor/futures-io/README.md create mode 100644 vendor/futures-io/src/lib.rs create mode 100644 vendor/futures-macro/.cargo-checksum.json create mode 100644 vendor/futures-macro/Cargo.toml create mode 100644 vendor/futures-macro/LICENSE-APACHE create mode 100644 vendor/futures-macro/LICENSE-MIT create mode 100644 vendor/futures-macro/src/executor.rs create mode 100644 vendor/futures-macro/src/join.rs create mode 100644 vendor/futures-macro/src/lib.rs create mode 100644 vendor/futures-macro/src/select.rs create mode 100644 vendor/futures-macro/src/stream_select.rs create mode 100644 vendor/futures-sink/.cargo-checksum.json create mode 100644 vendor/futures-sink/Cargo.toml create mode 100644 vendor/futures-sink/LICENSE-APACHE create mode 100644 vendor/futures-sink/LICENSE-MIT create mode 100644 vendor/futures-sink/README.md create mode 100644 vendor/futures-sink/src/lib.rs create mode 100644 vendor/futures-task/.cargo-checksum.json create mode 100644 vendor/futures-task/Cargo.toml create mode 100644 vendor/futures-task/LICENSE-APACHE create mode 100644 vendor/futures-task/LICENSE-MIT create mode 100644 vendor/futures-task/README.md create mode 100644 vendor/futures-task/build.rs create mode 100644 vendor/futures-task/no_atomic_cas.rs create mode 100644 vendor/futures-task/src/arc_wake.rs create mode 100644 vendor/futures-task/src/future_obj.rs create mode 100644 vendor/futures-task/src/lib.rs create mode 100644 vendor/futures-task/src/noop_waker.rs create mode 100644 vendor/futures-task/src/spawn.rs create mode 100644 vendor/futures-task/src/waker.rs create mode 100644 vendor/futures-task/src/waker_ref.rs create mode 100644 vendor/futures-util/.cargo-checksum.json create mode 100644 vendor/futures-util/Cargo.toml create mode 100644 vendor/futures-util/LICENSE-APACHE create mode 100644 vendor/futures-util/LICENSE-MIT create mode 100644 vendor/futures-util/README.md create mode 100644 vendor/futures-util/benches/futures_unordered.rs create mode 100644 vendor/futures-util/benches_disabled/bilock.rs create mode 100644 vendor/futures-util/build.rs create mode 100644 vendor/futures-util/no_atomic_cas.rs create mode 100644 vendor/futures-util/src/abortable.rs create mode 100644 vendor/futures-util/src/async_await/join_mod.rs create mode 100644 vendor/futures-util/src/async_await/mod.rs create mode 100644 vendor/futures-util/src/async_await/pending.rs create mode 100644 vendor/futures-util/src/async_await/poll.rs create mode 100644 vendor/futures-util/src/async_await/random.rs create mode 100644 vendor/futures-util/src/async_await/select_mod.rs create mode 100644 vendor/futures-util/src/async_await/stream_select_mod.rs create mode 100644 vendor/futures-util/src/compat/compat01as03.rs create mode 100644 vendor/futures-util/src/compat/compat03as01.rs create mode 100644 vendor/futures-util/src/compat/executor.rs create mode 100644 vendor/futures-util/src/compat/mod.rs create mode 100644 vendor/futures-util/src/fns.rs create mode 100644 vendor/futures-util/src/future/abortable.rs create mode 100644 vendor/futures-util/src/future/either.rs create mode 100644 vendor/futures-util/src/future/future/catch_unwind.rs create mode 100644 vendor/futures-util/src/future/future/flatten.rs create mode 100644 vendor/futures-util/src/future/future/fuse.rs create mode 100644 vendor/futures-util/src/future/future/map.rs create mode 100644 vendor/futures-util/src/future/future/mod.rs create mode 100644 vendor/futures-util/src/future/future/remote_handle.rs create mode 100644 vendor/futures-util/src/future/future/shared.rs create mode 100644 vendor/futures-util/src/future/join.rs create mode 100644 vendor/futures-util/src/future/join_all.rs create mode 100644 vendor/futures-util/src/future/lazy.rs create mode 100644 vendor/futures-util/src/future/maybe_done.rs create mode 100644 vendor/futures-util/src/future/mod.rs create mode 100644 vendor/futures-util/src/future/option.rs create mode 100644 vendor/futures-util/src/future/pending.rs create mode 100644 vendor/futures-util/src/future/poll_fn.rs create mode 100644 vendor/futures-util/src/future/poll_immediate.rs create mode 100644 vendor/futures-util/src/future/ready.rs create mode 100644 vendor/futures-util/src/future/select.rs create mode 100644 vendor/futures-util/src/future/select_all.rs create mode 100644 vendor/futures-util/src/future/select_ok.rs create mode 100644 vendor/futures-util/src/future/try_future/into_future.rs create mode 100644 vendor/futures-util/src/future/try_future/mod.rs create mode 100644 vendor/futures-util/src/future/try_future/try_flatten.rs create mode 100644 vendor/futures-util/src/future/try_future/try_flatten_err.rs create mode 100644 vendor/futures-util/src/future/try_join.rs create mode 100644 vendor/futures-util/src/future/try_join_all.rs create mode 100644 vendor/futures-util/src/future/try_maybe_done.rs create mode 100644 vendor/futures-util/src/future/try_select.rs create mode 100644 vendor/futures-util/src/io/allow_std.rs create mode 100644 vendor/futures-util/src/io/buf_reader.rs create mode 100644 vendor/futures-util/src/io/buf_writer.rs create mode 100644 vendor/futures-util/src/io/chain.rs create mode 100644 vendor/futures-util/src/io/close.rs create mode 100644 vendor/futures-util/src/io/copy.rs create mode 100644 vendor/futures-util/src/io/copy_buf.rs create mode 100644 vendor/futures-util/src/io/cursor.rs create mode 100644 vendor/futures-util/src/io/empty.rs create mode 100644 vendor/futures-util/src/io/fill_buf.rs create mode 100644 vendor/futures-util/src/io/flush.rs create mode 100644 vendor/futures-util/src/io/into_sink.rs create mode 100644 vendor/futures-util/src/io/line_writer.rs create mode 100644 vendor/futures-util/src/io/lines.rs create mode 100644 vendor/futures-util/src/io/mod.rs create mode 100644 vendor/futures-util/src/io/read.rs create mode 100644 vendor/futures-util/src/io/read_exact.rs create mode 100644 vendor/futures-util/src/io/read_line.rs create mode 100644 vendor/futures-util/src/io/read_to_end.rs create mode 100644 vendor/futures-util/src/io/read_to_string.rs create mode 100644 vendor/futures-util/src/io/read_until.rs create mode 100644 vendor/futures-util/src/io/read_vectored.rs create mode 100644 vendor/futures-util/src/io/repeat.rs create mode 100644 vendor/futures-util/src/io/seek.rs create mode 100644 vendor/futures-util/src/io/sink.rs create mode 100644 vendor/futures-util/src/io/split.rs create mode 100644 vendor/futures-util/src/io/take.rs create mode 100644 vendor/futures-util/src/io/window.rs create mode 100644 vendor/futures-util/src/io/write.rs create mode 100644 vendor/futures-util/src/io/write_all.rs create mode 100644 vendor/futures-util/src/io/write_all_vectored.rs create mode 100644 vendor/futures-util/src/io/write_vectored.rs create mode 100644 vendor/futures-util/src/lib.rs create mode 100644 vendor/futures-util/src/lock/bilock.rs create mode 100644 vendor/futures-util/src/lock/mod.rs create mode 100644 vendor/futures-util/src/lock/mutex.rs create mode 100644 vendor/futures-util/src/never.rs create mode 100644 vendor/futures-util/src/sink/buffer.rs create mode 100644 vendor/futures-util/src/sink/close.rs create mode 100644 vendor/futures-util/src/sink/drain.rs create mode 100644 vendor/futures-util/src/sink/err_into.rs create mode 100644 vendor/futures-util/src/sink/fanout.rs create mode 100644 vendor/futures-util/src/sink/feed.rs create mode 100644 vendor/futures-util/src/sink/flush.rs create mode 100644 vendor/futures-util/src/sink/map_err.rs create mode 100644 vendor/futures-util/src/sink/mod.rs create mode 100644 vendor/futures-util/src/sink/send.rs create mode 100644 vendor/futures-util/src/sink/send_all.rs create mode 100644 vendor/futures-util/src/sink/unfold.rs create mode 100644 vendor/futures-util/src/sink/with.rs create mode 100644 vendor/futures-util/src/sink/with_flat_map.rs create mode 100644 vendor/futures-util/src/stream/abortable.rs create mode 100644 vendor/futures-util/src/stream/empty.rs create mode 100644 vendor/futures-util/src/stream/futures_ordered.rs create mode 100644 vendor/futures-util/src/stream/futures_unordered/abort.rs create mode 100644 vendor/futures-util/src/stream/futures_unordered/iter.rs create mode 100644 vendor/futures-util/src/stream/futures_unordered/mod.rs create mode 100644 vendor/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs create mode 100644 vendor/futures-util/src/stream/futures_unordered/task.rs create mode 100644 vendor/futures-util/src/stream/iter.rs create mode 100644 vendor/futures-util/src/stream/mod.rs create mode 100644 vendor/futures-util/src/stream/once.rs create mode 100644 vendor/futures-util/src/stream/pending.rs create mode 100644 vendor/futures-util/src/stream/poll_fn.rs create mode 100644 vendor/futures-util/src/stream/poll_immediate.rs create mode 100644 vendor/futures-util/src/stream/repeat.rs create mode 100644 vendor/futures-util/src/stream/repeat_with.rs create mode 100644 vendor/futures-util/src/stream/select.rs create mode 100644 vendor/futures-util/src/stream/select_all.rs create mode 100644 vendor/futures-util/src/stream/select_with_strategy.rs create mode 100644 vendor/futures-util/src/stream/stream/all.rs create mode 100644 vendor/futures-util/src/stream/stream/any.rs create mode 100644 vendor/futures-util/src/stream/stream/buffer_unordered.rs create mode 100644 vendor/futures-util/src/stream/stream/buffered.rs create mode 100644 vendor/futures-util/src/stream/stream/catch_unwind.rs create mode 100644 vendor/futures-util/src/stream/stream/chain.rs create mode 100644 vendor/futures-util/src/stream/stream/chunks.rs create mode 100644 vendor/futures-util/src/stream/stream/collect.rs create mode 100644 vendor/futures-util/src/stream/stream/concat.rs create mode 100644 vendor/futures-util/src/stream/stream/count.rs create mode 100644 vendor/futures-util/src/stream/stream/cycle.rs create mode 100644 vendor/futures-util/src/stream/stream/enumerate.rs create mode 100644 vendor/futures-util/src/stream/stream/filter.rs create mode 100644 vendor/futures-util/src/stream/stream/filter_map.rs create mode 100644 vendor/futures-util/src/stream/stream/flatten.rs create mode 100644 vendor/futures-util/src/stream/stream/fold.rs create mode 100644 vendor/futures-util/src/stream/stream/for_each.rs create mode 100644 vendor/futures-util/src/stream/stream/for_each_concurrent.rs create mode 100644 vendor/futures-util/src/stream/stream/forward.rs create mode 100644 vendor/futures-util/src/stream/stream/fuse.rs create mode 100644 vendor/futures-util/src/stream/stream/into_future.rs create mode 100644 vendor/futures-util/src/stream/stream/map.rs create mode 100644 vendor/futures-util/src/stream/stream/mod.rs create mode 100644 vendor/futures-util/src/stream/stream/next.rs create mode 100644 vendor/futures-util/src/stream/stream/peek.rs create mode 100644 vendor/futures-util/src/stream/stream/ready_chunks.rs create mode 100644 vendor/futures-util/src/stream/stream/scan.rs create mode 100644 vendor/futures-util/src/stream/stream/select_next_some.rs create mode 100644 vendor/futures-util/src/stream/stream/skip.rs create mode 100644 vendor/futures-util/src/stream/stream/skip_while.rs create mode 100644 vendor/futures-util/src/stream/stream/split.rs create mode 100644 vendor/futures-util/src/stream/stream/take.rs create mode 100644 vendor/futures-util/src/stream/stream/take_until.rs create mode 100644 vendor/futures-util/src/stream/stream/take_while.rs create mode 100644 vendor/futures-util/src/stream/stream/then.rs create mode 100644 vendor/futures-util/src/stream/stream/unzip.rs create mode 100644 vendor/futures-util/src/stream/stream/zip.rs create mode 100644 vendor/futures-util/src/stream/try_stream/and_then.rs create mode 100644 vendor/futures-util/src/stream/try_stream/into_async_read.rs create mode 100644 vendor/futures-util/src/stream/try_stream/into_stream.rs create mode 100644 vendor/futures-util/src/stream/try_stream/mod.rs create mode 100644 vendor/futures-util/src/stream/try_stream/or_else.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_buffer_unordered.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_buffered.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_chunks.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_collect.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_concat.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_filter.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_filter_map.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_flatten.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_fold.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_for_each.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_for_each_concurrent.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_next.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_skip_while.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_take_while.rs create mode 100644 vendor/futures-util/src/stream/try_stream/try_unfold.rs create mode 100644 vendor/futures-util/src/stream/unfold.rs create mode 100644 vendor/futures-util/src/task/mod.rs create mode 100644 vendor/futures-util/src/task/spawn.rs create mode 100644 vendor/futures-util/src/unfold_state.rs create mode 100644 vendor/futures/.cargo-checksum.json create mode 100644 vendor/futures/Cargo.toml create mode 100644 vendor/futures/LICENSE-APACHE create mode 100644 vendor/futures/LICENSE-MIT create mode 100644 vendor/futures/src/lib.rs create mode 100644 vendor/futures/tests/_require_features.rs create mode 100644 vendor/futures/tests/async_await_macros.rs create mode 100644 vendor/futures/tests/auto_traits.rs create mode 100644 vendor/futures/tests/compat.rs create mode 100644 vendor/futures/tests/eager_drop.rs create mode 100644 vendor/futures/tests/eventual.rs create mode 100644 vendor/futures/tests/future_abortable.rs create mode 100644 vendor/futures/tests/future_basic_combinators.rs create mode 100644 vendor/futures/tests/future_fuse.rs create mode 100644 vendor/futures/tests/future_inspect.rs create mode 100644 vendor/futures/tests/future_join_all.rs create mode 100644 vendor/futures/tests/future_obj.rs create mode 100644 vendor/futures/tests/future_select_all.rs create mode 100644 vendor/futures/tests/future_select_ok.rs create mode 100644 vendor/futures/tests/future_shared.rs create mode 100644 vendor/futures/tests/future_try_flatten_stream.rs create mode 100644 vendor/futures/tests/future_try_join_all.rs create mode 100644 vendor/futures/tests/io_buf_reader.rs create mode 100644 vendor/futures/tests/io_buf_writer.rs create mode 100644 vendor/futures/tests/io_cursor.rs create mode 100644 vendor/futures/tests/io_line_writer.rs create mode 100644 vendor/futures/tests/io_lines.rs create mode 100644 vendor/futures/tests/io_read.rs create mode 100644 vendor/futures/tests/io_read_exact.rs create mode 100644 vendor/futures/tests/io_read_line.rs create mode 100644 vendor/futures/tests/io_read_to_end.rs create mode 100644 vendor/futures/tests/io_read_to_string.rs create mode 100644 vendor/futures/tests/io_read_until.rs create mode 100644 vendor/futures/tests/io_window.rs create mode 100644 vendor/futures/tests/io_write.rs create mode 100644 vendor/futures/tests/lock_mutex.rs create mode 100644 vendor/futures/tests/macro_comma_support.rs create mode 100644 vendor/futures/tests/object_safety.rs create mode 100644 vendor/futures/tests/oneshot.rs create mode 100644 vendor/futures/tests/ready_queue.rs create mode 100644 vendor/futures/tests/recurse.rs create mode 100644 vendor/futures/tests/sink.rs create mode 100644 vendor/futures/tests/sink_fanout.rs create mode 100644 vendor/futures/tests/stream.rs create mode 100644 vendor/futures/tests/stream_abortable.rs create mode 100644 vendor/futures/tests/stream_buffer_unordered.rs create mode 100644 vendor/futures/tests/stream_catch_unwind.rs create mode 100644 vendor/futures/tests/stream_futures_ordered.rs create mode 100644 vendor/futures/tests/stream_futures_unordered.rs create mode 100644 vendor/futures/tests/stream_into_async_read.rs create mode 100644 vendor/futures/tests/stream_peekable.rs create mode 100644 vendor/futures/tests/stream_select_all.rs create mode 100644 vendor/futures/tests/stream_select_next_some.rs create mode 100644 vendor/futures/tests/stream_split.rs create mode 100644 vendor/futures/tests/stream_try_stream.rs create mode 100644 vendor/futures/tests/stream_unfold.rs create mode 100644 vendor/futures/tests/task_arc_wake.rs create mode 100644 vendor/futures/tests/task_atomic_waker.rs create mode 100644 vendor/futures/tests/test_macro.rs create mode 100644 vendor/futures/tests/try_join.rs create mode 100644 vendor/futures/tests_disabled/all.rs create mode 100644 vendor/futures/tests_disabled/bilock.rs create mode 100644 vendor/futures/tests_disabled/stream.rs delete mode 100644 vendor/globwalk/.cargo-checksum.json delete mode 100644 vendor/globwalk/Cargo.lock delete mode 100644 vendor/globwalk/Cargo.toml delete mode 100644 vendor/globwalk/LICENSE delete mode 100644 vendor/globwalk/README.md delete mode 100644 vendor/globwalk/appveyor.yml delete mode 100644 vendor/globwalk/examples/list.rs delete mode 100644 vendor/globwalk/src/doctests.rs delete mode 100644 vendor/globwalk/src/lib.rs delete mode 100644 vendor/globwalk/tests/docs.rs create mode 100644 vendor/hashbrown-0.11.2/.cargo-checksum.json create mode 100644 vendor/hashbrown-0.11.2/CHANGELOG.md create mode 100644 vendor/hashbrown-0.11.2/Cargo.toml rename vendor/{crossbeam-epoch-0.8.2 => hashbrown-0.11.2}/LICENSE-APACHE (100%) create mode 100644 vendor/hashbrown-0.11.2/LICENSE-MIT create mode 100644 vendor/hashbrown-0.11.2/README.md create mode 100644 vendor/hashbrown-0.11.2/benches/bench.rs create mode 100644 vendor/hashbrown-0.11.2/clippy.toml create mode 100644 vendor/hashbrown-0.11.2/src/external_trait_impls/mod.rs create mode 100644 vendor/hashbrown-0.11.2/src/external_trait_impls/rayon/helpers.rs create mode 100644 vendor/hashbrown-0.11.2/src/external_trait_impls/rayon/map.rs create mode 100644 vendor/hashbrown-0.11.2/src/external_trait_impls/rayon/mod.rs create mode 100644 vendor/hashbrown-0.11.2/src/external_trait_impls/rayon/raw.rs create mode 100644 vendor/hashbrown-0.11.2/src/external_trait_impls/rayon/set.rs create mode 100644 vendor/hashbrown-0.11.2/src/external_trait_impls/serde.rs create mode 100644 vendor/hashbrown-0.11.2/src/lib.rs create mode 100644 vendor/hashbrown-0.11.2/src/macros.rs create mode 100644 vendor/hashbrown-0.11.2/src/map.rs create mode 100644 vendor/hashbrown-0.11.2/src/raw/alloc.rs create mode 100644 vendor/hashbrown-0.11.2/src/raw/bitmask.rs create mode 100644 vendor/hashbrown-0.11.2/src/raw/generic.rs create mode 100644 vendor/hashbrown-0.11.2/src/raw/mod.rs create mode 100644 vendor/hashbrown-0.11.2/src/raw/sse2.rs create mode 100644 vendor/hashbrown-0.11.2/src/rustc_entry.rs create mode 100644 vendor/hashbrown-0.11.2/src/scopeguard.rs create mode 100644 vendor/hashbrown-0.11.2/src/set.rs create mode 100644 vendor/hashbrown-0.11.2/tests/hasher.rs create mode 100644 vendor/hashbrown-0.11.2/tests/rayon.rs create mode 100644 vendor/hashbrown-0.11.2/tests/serde.rs create mode 100644 vendor/hashbrown-0.11.2/tests/set.rs create mode 100644 vendor/hashbrown/benches/insert_unique_unchecked.rs create mode 100644 vendor/indexmap/RELEASES.rst create mode 100644 vendor/indexmap/src/rustc.rs create mode 100644 vendor/libc/src/unix/newlib/horizon/mod.rs delete mode 100644 vendor/markup5ever/data/entities.json create mode 100644 vendor/markup5ever/entities.rs delete mode 100644 vendor/maybe-uninit/.cargo-checksum.json delete mode 100644 vendor/maybe-uninit/README.md delete mode 100644 vendor/maybe-uninit/build.rs delete mode 100644 vendor/maybe-uninit/src/lib.rs delete mode 100644 vendor/maybe-uninit/src/maybe_uninit.rs delete mode 100644 vendor/maybe-uninit/tests/doesnt_drop.rs delete mode 100644 vendor/md-5/Cargo.lock delete mode 100644 vendor/md-5/benches/lib.rs create mode 100644 vendor/md-5/benches/mod.rs delete mode 100644 vendor/md-5/examples/md5sum.rs rename vendor/md-5/src/{utils.rs => compress.rs} (79%) delete mode 100644 vendor/md-5/src/consts.rs delete mode 100644 vendor/md-5/tests/data/one_million_a.bin delete mode 100644 vendor/md-5/tests/lib.rs create mode 100644 vendor/md-5/tests/mod.rs delete mode 100644 vendor/memoffset-0.5.5/.cargo-checksum.json delete mode 100644 vendor/memoffset-0.5.5/Cargo.toml delete mode 100644 vendor/memoffset-0.5.5/LICENSE delete mode 100644 vendor/memoffset-0.5.5/README.md delete mode 100644 vendor/memoffset-0.5.5/build.rs delete mode 100644 vendor/memoffset-0.5.5/src/lib.rs delete mode 100644 vendor/memoffset-0.5.5/src/offset_of.rs delete mode 100644 vendor/memoffset-0.5.5/src/raw_field.rs delete mode 100644 vendor/memoffset-0.5.5/src/span_of.rs create mode 100644 vendor/minimal-lexical/.cargo-checksum.json create mode 100644 vendor/minimal-lexical/CHANGELOG create mode 100644 vendor/minimal-lexical/CODE_OF_CONDUCT.md create mode 100644 vendor/minimal-lexical/Cargo.toml create mode 100644 vendor/minimal-lexical/LICENSE-APACHE rename vendor/{cargo_metadata-0.12.0 => minimal-lexical}/LICENSE-MIT (100%) create mode 100644 vendor/minimal-lexical/LICENSE.md create mode 100644 vendor/minimal-lexical/README.md create mode 100644 vendor/minimal-lexical/clippy.toml create mode 100644 vendor/minimal-lexical/rustfmt.toml create mode 100644 vendor/minimal-lexical/src/bellerophon.rs create mode 100644 vendor/minimal-lexical/src/bigint.rs create mode 100644 vendor/minimal-lexical/src/extended_float.rs create mode 100644 vendor/minimal-lexical/src/fpu.rs create mode 100644 vendor/minimal-lexical/src/heapvec.rs create mode 100644 vendor/minimal-lexical/src/lemire.rs create mode 100644 vendor/minimal-lexical/src/lib.rs create mode 100644 vendor/minimal-lexical/src/libm.rs create mode 100644 vendor/minimal-lexical/src/mask.rs create mode 100644 vendor/minimal-lexical/src/num.rs create mode 100644 vendor/minimal-lexical/src/number.rs create mode 100644 vendor/minimal-lexical/src/parse.rs create mode 100644 vendor/minimal-lexical/src/rounding.rs create mode 100644 vendor/minimal-lexical/src/slow.rs create mode 100644 vendor/minimal-lexical/src/stackvec.rs create mode 100644 vendor/minimal-lexical/src/table.rs create mode 100644 vendor/minimal-lexical/src/table_bellerophon.rs create mode 100644 vendor/minimal-lexical/src/table_lemire.rs create mode 100644 vendor/minimal-lexical/src/table_small.rs create mode 100644 vendor/minimal-lexical/tests/bellerophon.rs create mode 100644 vendor/minimal-lexical/tests/bellerophon_tests.rs create mode 100644 vendor/minimal-lexical/tests/integration_tests.rs create mode 100644 vendor/minimal-lexical/tests/lemire_tests.rs create mode 100644 vendor/minimal-lexical/tests/libm_tests.rs create mode 100644 vendor/minimal-lexical/tests/mask_tests.rs create mode 100644 vendor/minimal-lexical/tests/number_tests.rs create mode 100644 vendor/minimal-lexical/tests/parse_tests.rs create mode 100644 vendor/minimal-lexical/tests/rounding_tests.rs create mode 100644 vendor/minimal-lexical/tests/slow_tests.rs create mode 100644 vendor/minimal-lexical/tests/stackvec.rs create mode 100644 vendor/minimal-lexical/tests/vec_tests.rs create mode 100644 vendor/nom/.cargo-checksum.json create mode 100644 vendor/nom/CHANGELOG.md create mode 100644 vendor/nom/Cargo.lock create mode 100644 vendor/nom/Cargo.toml create mode 100644 vendor/nom/LICENSE create mode 100644 vendor/nom/README.md create mode 100644 vendor/nom/build.rs create mode 100644 vendor/nom/doc/nom_recipes.md create mode 100644 vendor/nom/src/bits/complete.rs create mode 100644 vendor/nom/src/bits/mod.rs create mode 100644 vendor/nom/src/bits/streaming.rs create mode 100644 vendor/nom/src/branch/mod.rs create mode 100644 vendor/nom/src/branch/tests.rs create mode 100644 vendor/nom/src/bytes/complete.rs create mode 100644 vendor/nom/src/bytes/mod.rs create mode 100644 vendor/nom/src/bytes/streaming.rs create mode 100644 vendor/nom/src/bytes/tests.rs create mode 100644 vendor/nom/src/character/complete.rs create mode 100644 vendor/nom/src/character/mod.rs create mode 100644 vendor/nom/src/character/streaming.rs create mode 100644 vendor/nom/src/character/tests.rs create mode 100644 vendor/nom/src/combinator/mod.rs create mode 100644 vendor/nom/src/combinator/tests.rs create mode 100644 vendor/nom/src/error.rs create mode 100644 vendor/nom/src/internal.rs create mode 100644 vendor/nom/src/lib.rs create mode 100644 vendor/nom/src/multi/mod.rs create mode 100644 vendor/nom/src/multi/tests.rs create mode 100644 vendor/nom/src/number/complete.rs create mode 100644 vendor/nom/src/number/mod.rs create mode 100644 vendor/nom/src/number/streaming.rs create mode 100644 vendor/nom/src/sequence/mod.rs create mode 100644 vendor/nom/src/sequence/tests.rs create mode 100644 vendor/nom/src/str.rs create mode 100644 vendor/nom/src/traits.rs create mode 100644 vendor/nom/tests/arithmetic.rs create mode 100644 vendor/nom/tests/arithmetic_ast.rs create mode 100644 vendor/nom/tests/css.rs create mode 100644 vendor/nom/tests/custom_errors.rs create mode 100644 vendor/nom/tests/escaped.rs create mode 100644 vendor/nom/tests/float.rs create mode 100644 vendor/nom/tests/fnmut.rs create mode 100644 vendor/nom/tests/ini.rs create mode 100644 vendor/nom/tests/ini_str.rs create mode 100644 vendor/nom/tests/issues.rs create mode 100644 vendor/nom/tests/json.rs create mode 100644 vendor/nom/tests/mp4.rs create mode 100644 vendor/nom/tests/multiline.rs create mode 100644 vendor/nom/tests/overflow.rs create mode 100644 vendor/nom/tests/reborrow_fold.rs create mode 100644 vendor/num_cpus/fixtures/cgroups/proc/cgroups/mountinfo_multi_opt create mode 100644 vendor/num_cpus/fixtures/cgroups/proc/cgroups/mountinfo_zero_opt delete mode 100644 vendor/object-0.27.1/.cargo-checksum.json delete mode 100644 vendor/object-0.27.1/src/read/macho/dyld_cache.rs create mode 100644 vendor/object/.cargo-checksum.json rename vendor/{object-0.27.1 => object}/CHANGELOG.md (76%) rename vendor/{object-0.27.1 => object}/Cargo.toml (69%) rename vendor/{crossbeam-queue => object}/LICENSE-APACHE (100%) rename vendor/{object-0.27.1 => object}/LICENSE-MIT (100%) rename vendor/{object-0.27.1 => object}/README.md (100%) rename vendor/{object-0.27.1 => object}/clippy.toml (100%) rename vendor/{object-0.27.1 => object}/src/archive.rs (100%) rename vendor/{object-0.27.1 => object}/src/common.rs (94%) rename vendor/{object-0.27.1 => object}/src/elf.rs (100%) rename vendor/{object-0.27.1 => object}/src/endian.rs (100%) rename vendor/{object-0.27.1 => object}/src/lib.rs (98%) rename vendor/{object-0.27.1 => object}/src/macho.rs (98%) rename vendor/{object-0.27.1 => object}/src/pe.rs (100%) rename vendor/{object-0.27.1 => object}/src/pod.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/any.rs (97%) rename vendor/{object-0.27.1 => object}/src/read/archive.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/coff/comdat.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/coff/file.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/coff/mod.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/coff/relocation.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/coff/section.rs (92%) rename vendor/{object-0.27.1 => object}/src/read/coff/symbol.rs (98%) rename vendor/{object-0.27.1 => object}/src/read/elf/comdat.rs (95%) rename vendor/{object-0.27.1 => object}/src/read/elf/compression.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/elf/dynamic.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/elf/file.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/elf/hash.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/elf/mod.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/elf/note.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/elf/relocation.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/elf/section.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/elf/segment.rs (98%) rename vendor/{object-0.27.1 => object}/src/read/elf/symbol.rs (99%) rename vendor/{object-0.27.1 => object}/src/read/elf/version.rs (100%) create mode 100644 vendor/object/src/read/macho/dyld_cache.rs rename vendor/{object-0.27.1 => object}/src/read/macho/fat.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/macho/file.rs (84%) rename vendor/{object-0.27.1 => object}/src/read/macho/load_command.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/macho/mod.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/macho/relocation.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/macho/section.rs (96%) rename vendor/{object-0.27.1 => object}/src/read/macho/segment.rs (86%) rename vendor/{object-0.27.1 => object}/src/read/macho/symbol.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/mod.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/pe/data_directory.rs (85%) rename vendor/{object-0.27.1 => object}/src/read/pe/export.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/pe/file.rs (98%) rename vendor/{object-0.27.1 => object}/src/read/pe/import.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/pe/mod.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/pe/relocation.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/pe/rich.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/pe/section.rs (89%) rename vendor/{object-0.27.1 => object}/src/read/read_cache.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/read_ref.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/traits.rs (98%) rename vendor/{object-0.27.1 => object}/src/read/util.rs (100%) rename vendor/{object-0.27.1 => object}/src/read/wasm.rs (93%) rename vendor/{object-0.27.1 => object}/src/write/coff.rs (83%) rename vendor/{object-0.27.1 => object}/src/write/elf/mod.rs (100%) rename vendor/{object-0.27.1 => object}/src/write/elf/object.rs (99%) rename vendor/{object-0.27.1 => object}/src/write/elf/writer.rs (99%) rename vendor/{object-0.27.1 => object}/src/write/macho.rs (96%) rename vendor/{object-0.27.1 => object}/src/write/mod.rs (98%) rename vendor/{object-0.27.1 => object}/src/write/pe.rs (99%) rename vendor/{object-0.27.1 => object}/src/write/string.rs (95%) rename vendor/{object-0.27.1 => object}/src/write/util.rs (97%) rename vendor/{object-0.27.1 => object}/tests/integration.rs (61%) rename vendor/{object-0.27.1 => object}/tests/parse_self.rs (100%) create mode 100644 vendor/object/tests/read/coff.rs create mode 100644 vendor/object/tests/read/mod.rs rename vendor/{object-0.27.1 => object}/tests/round_trip/bss.rs (100%) create mode 100644 vendor/object/tests/round_trip/coff.rs rename vendor/{object-0.27.1 => object}/tests/round_trip/comdat.rs (99%) rename vendor/{object-0.27.1 => object}/tests/round_trip/common.rs (100%) rename vendor/{object-0.27.1 => object}/tests/round_trip/elf.rs (100%) rename vendor/{object-0.27.1 => object}/tests/round_trip/macho.rs (100%) rename vendor/{object-0.27.1 => object}/tests/round_trip/mod.rs (99%) create mode 100644 vendor/object/tests/round_trip/section_flags.rs rename vendor/{object-0.27.1 => object}/tests/round_trip/tls.rs (100%) delete mode 100644 vendor/opaque-debug-0.2.3/.cargo-checksum.json delete mode 100644 vendor/opaque-debug-0.2.3/src/lib.rs delete mode 100644 vendor/opaque-debug/tests/mod.rs rename vendor/{parking_lot => parking_lot-0.11.2}/.cargo-checksum.json (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/CHANGELOG.md (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/Cargo.toml (100%) rename vendor/{crossbeam-utils-0.7.2 => parking_lot-0.11.2}/LICENSE-APACHE (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/LICENSE-MIT (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/README.md (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/bors.toml (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/condvar.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/deadlock.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/elision.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/fair_mutex.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/lib.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/mutex.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/once.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/raw_fair_mutex.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/raw_mutex.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/raw_rwlock.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/remutex.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/rwlock.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/src/util.rs (100%) rename vendor/{parking_lot => parking_lot-0.11.2}/tests/issue_203.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/.cargo-checksum.json (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/Cargo.toml (100%) rename vendor/{maybe-uninit => parking_lot_core-0.8.5}/LICENSE-APACHE (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/LICENSE-MIT (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/build.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/lib.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/parking_lot.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/spinwait.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/generic.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/linux.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/mod.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/redox.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/sgx.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/unix.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/wasm.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/wasm_atomic.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/windows/keyed_event.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/windows/mod.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/thread_parker/windows/waitaddress.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/util.rs (100%) rename vendor/{parking_lot_core => parking_lot_core-0.8.5}/src/word_lock.rs (100%) create mode 100644 vendor/pin-project-lite/tests/README.md delete mode 100644 vendor/pin-project-lite/tests/expand/README.md create mode 100644 vendor/pin-utils/.cargo-checksum.json rename vendor/{opaque-debug-0.2.3 => pin-utils}/Cargo.toml (67%) create mode 100644 vendor/pin-utils/LICENSE-APACHE create mode 100644 vendor/pin-utils/LICENSE-MIT create mode 100644 vendor/pin-utils/README.md create mode 100644 vendor/pin-utils/src/lib.rs create mode 100644 vendor/pin-utils/src/projection.rs create mode 100644 vendor/pin-utils/src/stack_pin.rs create mode 100644 vendor/pin-utils/tests/projection.rs create mode 100644 vendor/pin-utils/tests/stack_pin.rs create mode 100644 vendor/pretty_assertions/CHANGELOG.md create mode 100644 vendor/pretty_assertions/Cargo.lock create mode 100644 vendor/pretty_assertions/examples/pretty_assertion_v0_6_1.png create mode 100755 vendor/pretty_assertions/scripts/check create mode 100755 vendor/pretty_assertions/scripts/install create mode 100755 vendor/pretty_assertions/scripts/publish delete mode 100644 vendor/pretty_assertions/src/format_changeset.rs create mode 100644 vendor/pretty_assertions/src/printer.rs delete mode 100644 vendor/pretty_assertions/tests/assert_eq.rs delete mode 100644 vendor/pretty_assertions/tests/assert_ne.rs delete mode 100644 vendor/pretty_assertions/tests/pretty_string.rs delete mode 100644 vendor/quote/benches/bench.rs create mode 100644 vendor/quote/rust-toolchain.toml delete mode 100644 vendor/semver-0.11.0/.cargo-checksum.json delete mode 100644 vendor/semver-0.11.0/Cargo.toml delete mode 100644 vendor/semver-0.11.0/README.md delete mode 100644 vendor/semver-0.11.0/src/diesel_impls.rs delete mode 100644 vendor/semver-0.11.0/src/lib.rs delete mode 100644 vendor/semver-0.11.0/src/version.rs delete mode 100644 vendor/semver-0.11.0/src/version_req.rs delete mode 100644 vendor/semver-0.11.0/tests/deprecation.rs delete mode 100644 vendor/semver-0.11.0/tests/diesel.rs delete mode 100644 vendor/semver-0.11.0/tests/serde.rs delete mode 100644 vendor/semver-parser/.cargo-checksum.json delete mode 100644 vendor/semver-parser/Cargo.lock delete mode 100644 vendor/semver-parser/Cargo.toml delete mode 100644 vendor/semver-parser/README.md delete mode 100644 vendor/semver-parser/src/generated.rs delete mode 100644 vendor/semver-parser/src/lexer.rs delete mode 100644 vendor/semver-parser/src/lib.rs delete mode 100644 vendor/semver-parser/src/main.rs delete mode 100644 vendor/semver-parser/src/parser.rs delete mode 100644 vendor/semver-parser/src/range.rs delete mode 100644 vendor/semver-parser/src/range_set.rs delete mode 100644 vendor/semver-parser/src/semver.pest delete mode 100644 vendor/semver-parser/src/version.rs delete mode 100644 vendor/semver-parser/tests/genpest.rs create mode 100644 vendor/serde/src/de/format.rs create mode 100644 vendor/serde_json/CONTRIBUTING.md create mode 100644 vendor/serde_json/tests/compiletest.rs create mode 100644 vendor/serde_json/tests/debug.rs create mode 100644 vendor/serde_json/tests/lexical.rs create mode 100644 vendor/serde_json/tests/lexical/algorithm.rs create mode 100644 vendor/serde_json/tests/lexical/exponent.rs create mode 100644 vendor/serde_json/tests/lexical/float.rs create mode 100644 vendor/serde_json/tests/lexical/math.rs create mode 100644 vendor/serde_json/tests/lexical/num.rs create mode 100644 vendor/serde_json/tests/lexical/parse.rs create mode 100644 vendor/serde_json/tests/lexical/rounding.rs create mode 100644 vendor/serde_json/tests/macros/mod.rs create mode 100644 vendor/serde_json/tests/map.rs create mode 100644 vendor/serde_json/tests/regression.rs create mode 100644 vendor/serde_json/tests/regression/issue520.rs create mode 100644 vendor/serde_json/tests/regression/issue795.rs create mode 100644 vendor/serde_json/tests/regression/issue845.rs create mode 100644 vendor/serde_json/tests/stream.rs create mode 100644 vendor/serde_json/tests/test.rs create mode 100644 vendor/serde_json/tests/ui/missing_colon.rs create mode 100644 vendor/serde_json/tests/ui/missing_colon.stderr create mode 100644 vendor/serde_json/tests/ui/missing_comma.rs create mode 100644 vendor/serde_json/tests/ui/missing_comma.stderr create mode 100644 vendor/serde_json/tests/ui/missing_value.rs create mode 100644 vendor/serde_json/tests/ui/missing_value.stderr create mode 100644 vendor/serde_json/tests/ui/not_found.rs create mode 100644 vendor/serde_json/tests/ui/not_found.stderr create mode 100644 vendor/serde_json/tests/ui/parse_expr.rs create mode 100644 vendor/serde_json/tests/ui/parse_expr.stderr create mode 100644 vendor/serde_json/tests/ui/parse_key.rs create mode 100644 vendor/serde_json/tests/ui/parse_key.stderr create mode 100644 vendor/serde_json/tests/ui/unexpected_after_array_element.rs create mode 100644 vendor/serde_json/tests/ui/unexpected_after_array_element.stderr create mode 100644 vendor/serde_json/tests/ui/unexpected_after_map_entry.rs create mode 100644 vendor/serde_json/tests/ui/unexpected_after_map_entry.stderr create mode 100644 vendor/serde_json/tests/ui/unexpected_colon.rs create mode 100644 vendor/serde_json/tests/ui/unexpected_colon.stderr create mode 100644 vendor/serde_json/tests/ui/unexpected_comma.rs create mode 100644 vendor/serde_json/tests/ui/unexpected_comma.stderr delete mode 100644 vendor/sha-1/Cargo.lock delete mode 100644 vendor/sha-1/benches/lib.rs create mode 100644 vendor/sha-1/benches/mod.rs delete mode 100644 vendor/sha-1/examples/sha1sum.rs delete mode 100644 vendor/sha-1/src/consts.rs delete mode 100644 vendor/sha-1/tests/data/one_million_a.bin delete mode 100644 vendor/sha-1/tests/lib.rs create mode 100644 vendor/sha-1/tests/mod.rs delete mode 100644 vendor/sha2/Cargo.lock create mode 100644 vendor/sha2/benches/mod.rs delete mode 100644 vendor/sha2/benches/sha256.rs delete mode 100644 vendor/sha2/benches/sha512.rs delete mode 100644 vendor/sha2/examples/sha256sum.rs delete mode 100644 vendor/sha2/examples/sha512sum.rs create mode 100644 vendor/sha2/src/core_api.rs create mode 100644 vendor/sha2/src/sha512/x86.rs delete mode 100644 vendor/sha2/tests/data/sha256_one_million_a.bin delete mode 100644 vendor/sha2/tests/data/sha512_one_million_a.bin delete mode 100644 vendor/sha2/tests/lib.rs create mode 100644 vendor/sha2/tests/mod.rs create mode 100644 vendor/slab/.cargo-checksum.json create mode 100644 vendor/slab/CHANGELOG.md rename vendor/{maybe-uninit => slab}/Cargo.toml (53%) create mode 100644 vendor/slab/LICENSE create mode 100644 vendor/slab/README.md create mode 100644 vendor/slab/src/lib.rs create mode 100644 vendor/slab/tests/slab.rs create mode 100644 vendor/smallvec/src/arbitrary.rs rename vendor/{strsim => strsim-0.8.0}/.cargo-checksum.json (100%) rename vendor/{strsim => strsim-0.8.0}/CHANGELOG.md (100%) rename vendor/{strsim => strsim-0.8.0}/Cargo.toml (100%) rename vendor/{strsim => strsim-0.8.0}/LICENSE (100%) rename vendor/{strsim => strsim-0.8.0}/README.md (100%) rename vendor/{strsim => strsim-0.8.0}/appveyor.yml (100%) rename vendor/{strsim => strsim-0.8.0}/benches/benches.rs (100%) rename vendor/{strsim => strsim-0.8.0}/dev (100%) rename vendor/{strsim => strsim-0.8.0}/src/lib.rs (100%) rename vendor/{strsim => strsim-0.8.0}/tests/lib.rs (100%) create mode 100644 vendor/syn/tests/regression.rs create mode 100644 vendor/syn/tests/regression/issue1108.rs delete mode 100644 vendor/tera/.cargo-checksum.json delete mode 100644 vendor/tera/CHANGELOG.md delete mode 100644 vendor/tera/Cargo.toml delete mode 100644 vendor/tera/LICENSE delete mode 100644 vendor/tera/README.md delete mode 100644 vendor/tera/docs/README.md delete mode 100644 vendor/tera/src/builtins/filters/array.rs delete mode 100644 vendor/tera/src/builtins/filters/common.rs delete mode 100644 vendor/tera/src/builtins/filters/mod.rs delete mode 100644 vendor/tera/src/builtins/filters/number.rs delete mode 100644 vendor/tera/src/builtins/filters/object.rs delete mode 100644 vendor/tera/src/builtins/filters/string.rs delete mode 100644 vendor/tera/src/builtins/functions.rs delete mode 100644 vendor/tera/src/builtins/mod.rs delete mode 100644 vendor/tera/src/builtins/testers.rs delete mode 100644 vendor/tera/src/context.rs delete mode 100644 vendor/tera/src/errors.rs delete mode 100644 vendor/tera/src/filter_utils.rs delete mode 100644 vendor/tera/src/lib.rs delete mode 100644 vendor/tera/src/macros.rs delete mode 100644 vendor/tera/src/parser/ast.rs delete mode 100644 vendor/tera/src/parser/mod.rs delete mode 100644 vendor/tera/src/parser/tera.pest delete mode 100644 vendor/tera/src/parser/tests/errors.rs delete mode 100644 vendor/tera/src/parser/tests/lexer.rs delete mode 100644 vendor/tera/src/parser/tests/mod.rs delete mode 100644 vendor/tera/src/parser/tests/parser.rs delete mode 100644 vendor/tera/src/parser/tests/whitespace.rs delete mode 100644 vendor/tera/src/parser/whitespace.rs delete mode 100644 vendor/tera/src/renderer/call_stack.rs delete mode 100644 vendor/tera/src/renderer/for_loop.rs delete mode 100644 vendor/tera/src/renderer/macros.rs delete mode 100644 vendor/tera/src/renderer/mod.rs delete mode 100644 vendor/tera/src/renderer/processor.rs delete mode 100644 vendor/tera/src/renderer/square_brackets.rs delete mode 100644 vendor/tera/src/renderer/stack_frame.rs delete mode 100644 vendor/tera/src/renderer/tests/basic.rs delete mode 100644 vendor/tera/src/renderer/tests/errors.rs delete mode 100644 vendor/tera/src/renderer/tests/inheritance.rs delete mode 100644 vendor/tera/src/renderer/tests/macros.rs delete mode 100644 vendor/tera/src/renderer/tests/mod.rs delete mode 100644 vendor/tera/src/renderer/tests/square_brackets.rs delete mode 100644 vendor/tera/src/renderer/tests/whitespace.rs delete mode 100644 vendor/tera/src/template.rs delete mode 100644 vendor/tera/src/tera.rs delete mode 100644 vendor/tera/src/utils.rs rename vendor/{textwrap => textwrap-0.11.0}/.cargo-checksum.json (100%) rename vendor/{textwrap => textwrap-0.11.0}/Cargo.toml (100%) rename vendor/{textwrap => textwrap-0.11.0}/LICENSE (100%) rename vendor/{textwrap => textwrap-0.11.0}/README.md (100%) rename vendor/{textwrap => textwrap-0.11.0}/benches/linear.rs (100%) rename vendor/{textwrap => textwrap-0.11.0}/examples/layout.rs (100%) rename vendor/{textwrap => textwrap-0.11.0}/examples/termwidth.rs (100%) rename vendor/{textwrap => textwrap-0.11.0}/src/indentation.rs (100%) rename vendor/{textwrap => textwrap-0.11.0}/src/lib.rs (100%) rename vendor/{textwrap => textwrap-0.11.0}/src/splitting.rs (100%) rename vendor/{textwrap => textwrap-0.11.0}/tests/version-numbers.rs (100%) create mode 100644 vendor/tokio/.cargo-checksum.json create mode 100644 vendor/tokio/CHANGELOG.md create mode 100644 vendor/tokio/Cargo.toml create mode 100644 vendor/tokio/LICENSE create mode 100644 vendor/tokio/README.md create mode 100644 vendor/tokio/build.rs create mode 100644 vendor/tokio/docs/reactor-refactor.md create mode 100644 vendor/tokio/src/blocking.rs create mode 100644 vendor/tokio/src/coop.rs create mode 100644 vendor/tokio/src/doc/mod.rs create mode 100644 vendor/tokio/src/doc/os.rs create mode 100644 vendor/tokio/src/doc/winapi.rs create mode 100644 vendor/tokio/src/fs/canonicalize.rs create mode 100644 vendor/tokio/src/fs/copy.rs create mode 100644 vendor/tokio/src/fs/create_dir.rs create mode 100644 vendor/tokio/src/fs/create_dir_all.rs create mode 100644 vendor/tokio/src/fs/dir_builder.rs create mode 100644 vendor/tokio/src/fs/file.rs create mode 100644 vendor/tokio/src/fs/file/tests.rs create mode 100644 vendor/tokio/src/fs/hard_link.rs create mode 100644 vendor/tokio/src/fs/metadata.rs create mode 100644 vendor/tokio/src/fs/mocks.rs create mode 100644 vendor/tokio/src/fs/mod.rs create mode 100644 vendor/tokio/src/fs/open_options.rs create mode 100644 vendor/tokio/src/fs/open_options/mock_open_options.rs create mode 100644 vendor/tokio/src/fs/read.rs create mode 100644 vendor/tokio/src/fs/read_dir.rs create mode 100644 vendor/tokio/src/fs/read_link.rs create mode 100644 vendor/tokio/src/fs/read_to_string.rs create mode 100644 vendor/tokio/src/fs/remove_dir.rs create mode 100644 vendor/tokio/src/fs/remove_dir_all.rs create mode 100644 vendor/tokio/src/fs/remove_file.rs create mode 100644 vendor/tokio/src/fs/rename.rs create mode 100644 vendor/tokio/src/fs/set_permissions.rs create mode 100644 vendor/tokio/src/fs/symlink.rs create mode 100644 vendor/tokio/src/fs/symlink_dir.rs create mode 100644 vendor/tokio/src/fs/symlink_file.rs create mode 100644 vendor/tokio/src/fs/symlink_metadata.rs create mode 100644 vendor/tokio/src/fs/write.rs create mode 100644 vendor/tokio/src/future/block_on.rs create mode 100644 vendor/tokio/src/future/maybe_done.rs create mode 100644 vendor/tokio/src/future/mod.rs create mode 100644 vendor/tokio/src/future/poll_fn.rs create mode 100644 vendor/tokio/src/future/ready.rs create mode 100644 vendor/tokio/src/future/trace.rs create mode 100644 vendor/tokio/src/future/try_join.rs create mode 100644 vendor/tokio/src/io/async_buf_read.rs create mode 100644 vendor/tokio/src/io/async_fd.rs create mode 100644 vendor/tokio/src/io/async_read.rs create mode 100644 vendor/tokio/src/io/async_seek.rs create mode 100644 vendor/tokio/src/io/async_write.rs create mode 100644 vendor/tokio/src/io/blocking.rs create mode 100644 vendor/tokio/src/io/driver/interest.rs create mode 100644 vendor/tokio/src/io/driver/mod.rs create mode 100644 vendor/tokio/src/io/driver/platform.rs create mode 100644 vendor/tokio/src/io/driver/ready.rs create mode 100644 vendor/tokio/src/io/driver/registration.rs create mode 100644 vendor/tokio/src/io/driver/scheduled_io.rs create mode 100644 vendor/tokio/src/io/mod.rs create mode 100644 vendor/tokio/src/io/poll_evented.rs create mode 100644 vendor/tokio/src/io/read_buf.rs create mode 100644 vendor/tokio/src/io/seek.rs create mode 100644 vendor/tokio/src/io/split.rs create mode 100644 vendor/tokio/src/io/stderr.rs create mode 100644 vendor/tokio/src/io/stdin.rs create mode 100644 vendor/tokio/src/io/stdio_common.rs create mode 100644 vendor/tokio/src/io/stdout.rs create mode 100644 vendor/tokio/src/io/util/async_buf_read_ext.rs create mode 100644 vendor/tokio/src/io/util/async_read_ext.rs create mode 100644 vendor/tokio/src/io/util/async_seek_ext.rs create mode 100644 vendor/tokio/src/io/util/async_write_ext.rs create mode 100644 vendor/tokio/src/io/util/buf_reader.rs create mode 100644 vendor/tokio/src/io/util/buf_stream.rs create mode 100644 vendor/tokio/src/io/util/buf_writer.rs create mode 100644 vendor/tokio/src/io/util/chain.rs create mode 100644 vendor/tokio/src/io/util/copy.rs create mode 100644 vendor/tokio/src/io/util/copy_bidirectional.rs create mode 100644 vendor/tokio/src/io/util/copy_buf.rs create mode 100644 vendor/tokio/src/io/util/empty.rs create mode 100644 vendor/tokio/src/io/util/flush.rs create mode 100644 vendor/tokio/src/io/util/lines.rs create mode 100644 vendor/tokio/src/io/util/mem.rs create mode 100644 vendor/tokio/src/io/util/mod.rs create mode 100644 vendor/tokio/src/io/util/read.rs create mode 100644 vendor/tokio/src/io/util/read_buf.rs create mode 100644 vendor/tokio/src/io/util/read_exact.rs create mode 100644 vendor/tokio/src/io/util/read_int.rs create mode 100644 vendor/tokio/src/io/util/read_line.rs create mode 100644 vendor/tokio/src/io/util/read_to_end.rs create mode 100644 vendor/tokio/src/io/util/read_to_string.rs create mode 100644 vendor/tokio/src/io/util/read_until.rs create mode 100644 vendor/tokio/src/io/util/repeat.rs create mode 100644 vendor/tokio/src/io/util/shutdown.rs create mode 100644 vendor/tokio/src/io/util/sink.rs create mode 100644 vendor/tokio/src/io/util/split.rs create mode 100644 vendor/tokio/src/io/util/take.rs create mode 100644 vendor/tokio/src/io/util/vec_with_initialized.rs create mode 100644 vendor/tokio/src/io/util/write.rs create mode 100644 vendor/tokio/src/io/util/write_all.rs create mode 100644 vendor/tokio/src/io/util/write_all_buf.rs create mode 100644 vendor/tokio/src/io/util/write_buf.rs create mode 100644 vendor/tokio/src/io/util/write_int.rs create mode 100644 vendor/tokio/src/io/util/write_vectored.rs create mode 100644 vendor/tokio/src/lib.rs create mode 100644 vendor/tokio/src/loom/mocked.rs create mode 100644 vendor/tokio/src/loom/mod.rs create mode 100644 vendor/tokio/src/loom/std/atomic_ptr.rs create mode 100644 vendor/tokio/src/loom/std/atomic_u16.rs create mode 100644 vendor/tokio/src/loom/std/atomic_u32.rs create mode 100644 vendor/tokio/src/loom/std/atomic_u64.rs create mode 100644 vendor/tokio/src/loom/std/atomic_u8.rs create mode 100644 vendor/tokio/src/loom/std/atomic_usize.rs create mode 100644 vendor/tokio/src/loom/std/mod.rs create mode 100644 vendor/tokio/src/loom/std/mutex.rs create mode 100644 vendor/tokio/src/loom/std/parking_lot.rs create mode 100644 vendor/tokio/src/loom/std/unsafe_cell.rs create mode 100644 vendor/tokio/src/macros/cfg.rs create mode 100644 vendor/tokio/src/macros/join.rs create mode 100644 vendor/tokio/src/macros/loom.rs create mode 100644 vendor/tokio/src/macros/mod.rs create mode 100644 vendor/tokio/src/macros/pin.rs create mode 100644 vendor/tokio/src/macros/ready.rs create mode 100644 vendor/tokio/src/macros/scoped_tls.rs create mode 100644 vendor/tokio/src/macros/select.rs create mode 100644 vendor/tokio/src/macros/support.rs create mode 100644 vendor/tokio/src/macros/thread_local.rs create mode 100644 vendor/tokio/src/macros/try_join.rs create mode 100644 vendor/tokio/src/net/addr.rs create mode 100644 vendor/tokio/src/net/lookup_host.rs create mode 100644 vendor/tokio/src/net/mod.rs create mode 100644 vendor/tokio/src/net/tcp/listener.rs create mode 100644 vendor/tokio/src/net/tcp/mod.rs create mode 100644 vendor/tokio/src/net/tcp/socket.rs create mode 100644 vendor/tokio/src/net/tcp/split.rs create mode 100644 vendor/tokio/src/net/tcp/split_owned.rs create mode 100644 vendor/tokio/src/net/tcp/stream.rs create mode 100644 vendor/tokio/src/net/udp.rs create mode 100644 vendor/tokio/src/net/unix/datagram/mod.rs create mode 100644 vendor/tokio/src/net/unix/datagram/socket.rs create mode 100644 vendor/tokio/src/net/unix/listener.rs create mode 100644 vendor/tokio/src/net/unix/mod.rs create mode 100644 vendor/tokio/src/net/unix/socketaddr.rs create mode 100644 vendor/tokio/src/net/unix/split.rs create mode 100644 vendor/tokio/src/net/unix/split_owned.rs create mode 100644 vendor/tokio/src/net/unix/stream.rs create mode 100644 vendor/tokio/src/net/unix/ucred.rs create mode 100644 vendor/tokio/src/net/windows/mod.rs create mode 100644 vendor/tokio/src/net/windows/named_pipe.rs create mode 100644 vendor/tokio/src/park/either.rs create mode 100644 vendor/tokio/src/park/mod.rs create mode 100644 vendor/tokio/src/park/thread.rs create mode 100644 vendor/tokio/src/process/kill.rs create mode 100644 vendor/tokio/src/process/mod.rs create mode 100644 vendor/tokio/src/process/unix/driver.rs create mode 100644 vendor/tokio/src/process/unix/mod.rs create mode 100644 vendor/tokio/src/process/unix/orphan.rs create mode 100644 vendor/tokio/src/process/unix/reap.rs create mode 100644 vendor/tokio/src/process/windows.rs create mode 100644 vendor/tokio/src/runtime/basic_scheduler.rs create mode 100644 vendor/tokio/src/runtime/blocking/mod.rs create mode 100644 vendor/tokio/src/runtime/blocking/pool.rs create mode 100644 vendor/tokio/src/runtime/blocking/schedule.rs create mode 100644 vendor/tokio/src/runtime/blocking/shutdown.rs create mode 100644 vendor/tokio/src/runtime/blocking/task.rs create mode 100644 vendor/tokio/src/runtime/builder.rs create mode 100644 vendor/tokio/src/runtime/context.rs create mode 100644 vendor/tokio/src/runtime/driver.rs create mode 100644 vendor/tokio/src/runtime/enter.rs create mode 100644 vendor/tokio/src/runtime/handle.rs create mode 100644 vendor/tokio/src/runtime/mod.rs create mode 100644 vendor/tokio/src/runtime/park.rs create mode 100644 vendor/tokio/src/runtime/queue.rs create mode 100644 vendor/tokio/src/runtime/spawner.rs create mode 100644 vendor/tokio/src/runtime/task/core.rs create mode 100644 vendor/tokio/src/runtime/task/error.rs create mode 100644 vendor/tokio/src/runtime/task/harness.rs create mode 100644 vendor/tokio/src/runtime/task/join.rs create mode 100644 vendor/tokio/src/runtime/task/mod.rs create mode 100644 vendor/tokio/src/runtime/task/raw.rs create mode 100644 vendor/tokio/src/runtime/task/stack.rs create mode 100644 vendor/tokio/src/runtime/task/state.rs create mode 100644 vendor/tokio/src/runtime/task/waker.rs create mode 100644 vendor/tokio/src/runtime/tests/loom_basic_scheduler.rs create mode 100644 vendor/tokio/src/runtime/tests/loom_blocking.rs create mode 100644 vendor/tokio/src/runtime/tests/loom_local.rs create mode 100644 vendor/tokio/src/runtime/tests/loom_oneshot.rs create mode 100644 vendor/tokio/src/runtime/tests/loom_pool.rs create mode 100644 vendor/tokio/src/runtime/tests/loom_queue.rs create mode 100644 vendor/tokio/src/runtime/tests/loom_shutdown_join.rs create mode 100644 vendor/tokio/src/runtime/tests/mod.rs create mode 100644 vendor/tokio/src/runtime/tests/queue.rs create mode 100644 vendor/tokio/src/runtime/tests/task.rs create mode 100644 vendor/tokio/src/runtime/tests/task_combinations.rs create mode 100644 vendor/tokio/src/runtime/thread_pool/atomic_cell.rs create mode 100644 vendor/tokio/src/runtime/thread_pool/idle.rs create mode 100644 vendor/tokio/src/runtime/thread_pool/mod.rs create mode 100644 vendor/tokio/src/runtime/thread_pool/worker.rs create mode 100644 vendor/tokio/src/signal/ctrl_c.rs create mode 100644 vendor/tokio/src/signal/mod.rs create mode 100644 vendor/tokio/src/signal/registry.rs create mode 100644 vendor/tokio/src/signal/reusable_box.rs create mode 100644 vendor/tokio/src/signal/unix.rs create mode 100644 vendor/tokio/src/signal/unix/driver.rs create mode 100644 vendor/tokio/src/signal/windows.rs create mode 100644 vendor/tokio/src/sync/barrier.rs create mode 100644 vendor/tokio/src/sync/batch_semaphore.rs create mode 100644 vendor/tokio/src/sync/broadcast.rs create mode 100644 vendor/tokio/src/sync/mod.rs create mode 100644 vendor/tokio/src/sync/mpsc/block.rs create mode 100644 vendor/tokio/src/sync/mpsc/bounded.rs create mode 100644 vendor/tokio/src/sync/mpsc/chan.rs create mode 100644 vendor/tokio/src/sync/mpsc/error.rs create mode 100644 vendor/tokio/src/sync/mpsc/list.rs create mode 100644 vendor/tokio/src/sync/mpsc/mod.rs create mode 100644 vendor/tokio/src/sync/mpsc/unbounded.rs create mode 100644 vendor/tokio/src/sync/mutex.rs create mode 100644 vendor/tokio/src/sync/notify.rs create mode 100644 vendor/tokio/src/sync/once_cell.rs create mode 100644 vendor/tokio/src/sync/oneshot.rs create mode 100644 vendor/tokio/src/sync/rwlock.rs create mode 100644 vendor/tokio/src/sync/rwlock/owned_read_guard.rs create mode 100644 vendor/tokio/src/sync/rwlock/owned_write_guard.rs create mode 100644 vendor/tokio/src/sync/rwlock/owned_write_guard_mapped.rs create mode 100644 vendor/tokio/src/sync/rwlock/read_guard.rs create mode 100644 vendor/tokio/src/sync/rwlock/write_guard.rs create mode 100644 vendor/tokio/src/sync/rwlock/write_guard_mapped.rs create mode 100644 vendor/tokio/src/sync/semaphore.rs create mode 100644 vendor/tokio/src/sync/task/atomic_waker.rs create mode 100644 vendor/tokio/src/sync/task/mod.rs create mode 100644 vendor/tokio/src/sync/tests/atomic_waker.rs create mode 100644 vendor/tokio/src/sync/tests/loom_atomic_waker.rs create mode 100644 vendor/tokio/src/sync/tests/loom_broadcast.rs create mode 100644 vendor/tokio/src/sync/tests/loom_list.rs create mode 100644 vendor/tokio/src/sync/tests/loom_mpsc.rs create mode 100644 vendor/tokio/src/sync/tests/loom_notify.rs create mode 100644 vendor/tokio/src/sync/tests/loom_oneshot.rs create mode 100644 vendor/tokio/src/sync/tests/loom_rwlock.rs create mode 100644 vendor/tokio/src/sync/tests/loom_semaphore_batch.rs create mode 100644 vendor/tokio/src/sync/tests/loom_watch.rs create mode 100644 vendor/tokio/src/sync/tests/mod.rs create mode 100644 vendor/tokio/src/sync/tests/semaphore_batch.rs create mode 100644 vendor/tokio/src/sync/watch.rs create mode 100644 vendor/tokio/src/task/blocking.rs create mode 100644 vendor/tokio/src/task/builder.rs create mode 100644 vendor/tokio/src/task/local.rs create mode 100644 vendor/tokio/src/task/mod.rs create mode 100644 vendor/tokio/src/task/spawn.rs create mode 100644 vendor/tokio/src/task/task_local.rs create mode 100644 vendor/tokio/src/task/unconstrained.rs create mode 100644 vendor/tokio/src/task/yield_now.rs create mode 100644 vendor/tokio/src/time/clock.rs create mode 100644 vendor/tokio/src/time/driver/entry.rs create mode 100644 vendor/tokio/src/time/driver/handle.rs create mode 100644 vendor/tokio/src/time/driver/mod.rs create mode 100644 vendor/tokio/src/time/driver/sleep.rs create mode 100644 vendor/tokio/src/time/driver/tests/mod.rs create mode 100644 vendor/tokio/src/time/driver/wheel/level.rs create mode 100644 vendor/tokio/src/time/driver/wheel/mod.rs create mode 100644 vendor/tokio/src/time/driver/wheel/stack.rs create mode 100644 vendor/tokio/src/time/error.rs create mode 100644 vendor/tokio/src/time/instant.rs create mode 100644 vendor/tokio/src/time/interval.rs create mode 100644 vendor/tokio/src/time/mod.rs create mode 100644 vendor/tokio/src/time/tests/mod.rs create mode 100644 vendor/tokio/src/time/tests/test_sleep.rs create mode 100644 vendor/tokio/src/time/timeout.rs create mode 100644 vendor/tokio/src/util/bit.rs create mode 100644 vendor/tokio/src/util/error.rs create mode 100644 vendor/tokio/src/util/linked_list.rs create mode 100644 vendor/tokio/src/util/mod.rs create mode 100644 vendor/tokio/src/util/pad.rs create mode 100644 vendor/tokio/src/util/rand.rs create mode 100644 vendor/tokio/src/util/slab.rs create mode 100644 vendor/tokio/src/util/trace.rs create mode 100644 vendor/tokio/src/util/try_lock.rs create mode 100644 vendor/tokio/src/util/wake.rs create mode 100644 vendor/tokio/tests/_require_full.rs create mode 100644 vendor/tokio/tests/async_send_sync.rs create mode 100644 vendor/tokio/tests/buffered.rs create mode 100644 vendor/tokio/tests/fs.rs create mode 100644 vendor/tokio/tests/fs_copy.rs create mode 100644 vendor/tokio/tests/fs_dir.rs create mode 100644 vendor/tokio/tests/fs_file.rs create mode 100644 vendor/tokio/tests/fs_link.rs create mode 100644 vendor/tokio/tests/io_async_fd.rs create mode 100644 vendor/tokio/tests/io_async_read.rs create mode 100644 vendor/tokio/tests/io_buf_reader.rs create mode 100644 vendor/tokio/tests/io_buf_writer.rs create mode 100644 vendor/tokio/tests/io_chain.rs create mode 100644 vendor/tokio/tests/io_copy.rs create mode 100644 vendor/tokio/tests/io_copy_bidirectional.rs create mode 100644 vendor/tokio/tests/io_driver.rs create mode 100644 vendor/tokio/tests/io_driver_drop.rs create mode 100644 vendor/tokio/tests/io_lines.rs create mode 100644 vendor/tokio/tests/io_mem_stream.rs create mode 100644 vendor/tokio/tests/io_read.rs create mode 100644 vendor/tokio/tests/io_read_buf.rs create mode 100644 vendor/tokio/tests/io_read_exact.rs create mode 100644 vendor/tokio/tests/io_read_line.rs create mode 100644 vendor/tokio/tests/io_read_to_end.rs create mode 100644 vendor/tokio/tests/io_read_to_string.rs create mode 100644 vendor/tokio/tests/io_read_until.rs create mode 100644 vendor/tokio/tests/io_split.rs create mode 100644 vendor/tokio/tests/io_take.rs create mode 100644 vendor/tokio/tests/io_write.rs create mode 100644 vendor/tokio/tests/io_write_all.rs create mode 100644 vendor/tokio/tests/io_write_all_buf.rs create mode 100644 vendor/tokio/tests/io_write_buf.rs create mode 100644 vendor/tokio/tests/io_write_int.rs create mode 100644 vendor/tokio/tests/macros_join.rs create mode 100644 vendor/tokio/tests/macros_pin.rs create mode 100644 vendor/tokio/tests/macros_select.rs create mode 100644 vendor/tokio/tests/macros_test.rs create mode 100644 vendor/tokio/tests/macros_try_join.rs create mode 100644 vendor/tokio/tests/named_pipe.rs create mode 100644 vendor/tokio/tests/net_bind_resource.rs create mode 100644 vendor/tokio/tests/net_lookup_host.rs create mode 100644 vendor/tokio/tests/no_rt.rs create mode 100644 vendor/tokio/tests/process_issue_2174.rs create mode 100644 vendor/tokio/tests/process_issue_42.rs create mode 100644 vendor/tokio/tests/process_kill_on_drop.rs create mode 100644 vendor/tokio/tests/process_smoke.rs create mode 100644 vendor/tokio/tests/rt_basic.rs create mode 100644 vendor/tokio/tests/rt_common.rs create mode 100644 vendor/tokio/tests/rt_handle_block_on.rs create mode 100644 vendor/tokio/tests/rt_threaded.rs create mode 100644 vendor/tokio/tests/signal_ctrl_c.rs create mode 100644 vendor/tokio/tests/signal_drop_recv.rs create mode 100644 vendor/tokio/tests/signal_drop_rt.rs create mode 100644 vendor/tokio/tests/signal_drop_signal.rs create mode 100644 vendor/tokio/tests/signal_multi_rt.rs create mode 100644 vendor/tokio/tests/signal_no_rt.rs create mode 100644 vendor/tokio/tests/signal_notify_both.rs create mode 100644 vendor/tokio/tests/signal_twice.rs create mode 100644 vendor/tokio/tests/signal_usr1.rs create mode 100644 vendor/tokio/tests/support/io_vec.rs create mode 100644 vendor/tokio/tests/support/mpsc_stream.rs create mode 100644 vendor/tokio/tests/support/signal.rs create mode 100644 vendor/tokio/tests/sync_barrier.rs create mode 100644 vendor/tokio/tests/sync_broadcast.rs create mode 100644 vendor/tokio/tests/sync_errors.rs create mode 100644 vendor/tokio/tests/sync_mpsc.rs create mode 100644 vendor/tokio/tests/sync_mutex.rs create mode 100644 vendor/tokio/tests/sync_mutex_owned.rs create mode 100644 vendor/tokio/tests/sync_notify.rs create mode 100644 vendor/tokio/tests/sync_once_cell.rs create mode 100644 vendor/tokio/tests/sync_oneshot.rs create mode 100644 vendor/tokio/tests/sync_rwlock.rs create mode 100644 vendor/tokio/tests/sync_semaphore.rs create mode 100644 vendor/tokio/tests/sync_semaphore_owned.rs create mode 100644 vendor/tokio/tests/sync_watch.rs create mode 100644 vendor/tokio/tests/task_abort.rs create mode 100644 vendor/tokio/tests/task_blocking.rs create mode 100644 vendor/tokio/tests/task_builder.rs create mode 100644 vendor/tokio/tests/task_local.rs create mode 100644 vendor/tokio/tests/task_local_set.rs create mode 100644 vendor/tokio/tests/tcp_accept.rs create mode 100644 vendor/tokio/tests/tcp_connect.rs create mode 100644 vendor/tokio/tests/tcp_echo.rs create mode 100644 vendor/tokio/tests/tcp_into_split.rs create mode 100644 vendor/tokio/tests/tcp_into_std.rs create mode 100644 vendor/tokio/tests/tcp_peek.rs create mode 100644 vendor/tokio/tests/tcp_shutdown.rs create mode 100644 vendor/tokio/tests/tcp_socket.rs create mode 100644 vendor/tokio/tests/tcp_split.rs create mode 100644 vendor/tokio/tests/tcp_stream.rs create mode 100644 vendor/tokio/tests/test_clock.rs create mode 100644 vendor/tokio/tests/time_interval.rs create mode 100644 vendor/tokio/tests/time_pause.rs create mode 100644 vendor/tokio/tests/time_rt.rs create mode 100644 vendor/tokio/tests/time_sleep.rs create mode 100644 vendor/tokio/tests/time_timeout.rs create mode 100644 vendor/tokio/tests/udp.rs create mode 100644 vendor/tokio/tests/uds_cred.rs create mode 100644 vendor/tokio/tests/uds_datagram.rs create mode 100644 vendor/tokio/tests/uds_split.rs create mode 100644 vendor/tokio/tests/uds_stream.rs create mode 100644 vendor/tracing-attributes/src/attr.rs create mode 100644 vendor/tracing-attributes/src/expand.rs create mode 100644 vendor/tracing-attributes/tests/ret.rs create mode 100644 vendor/tracing-subscriber/src/filter/layer_filters/combinator.rs rename vendor/tracing-subscriber/src/filter/{layer_filters.rs => layer_filters/mod.rs} (77%) create mode 100644 vendor/tracing-subscriber/tests/layer_filters/combinators.rs create mode 100644 vendor/tracing/benches/global_subscriber.rs create mode 100644 vendor/tracing/tests/enabled.rs mode change 100644 => 100755 vendor/unicode-segmentation/scripts/unicode.py mode change 100644 => 100755 vendor/unicode-segmentation/scripts/unicode_gen_breaktests.py create mode 100644 vendor/valuable/.cargo-checksum.json create mode 100644 vendor/valuable/Cargo.lock create mode 100644 vendor/valuable/Cargo.toml create mode 100644 vendor/valuable/benches/structable.rs create mode 100644 vendor/valuable/build.rs create mode 100644 vendor/valuable/examples/derive.rs create mode 100644 vendor/valuable/examples/hello_world.rs create mode 100644 vendor/valuable/examples/print.rs create mode 100644 vendor/valuable/no_atomic.rs create mode 100644 vendor/valuable/src/enumerable.rs create mode 100644 vendor/valuable/src/field.rs create mode 100644 vendor/valuable/src/lib.rs create mode 100644 vendor/valuable/src/listable.rs create mode 100644 vendor/valuable/src/mappable.rs create mode 100644 vendor/valuable/src/named_values.rs create mode 100644 vendor/valuable/src/slice.rs create mode 100644 vendor/valuable/src/structable.rs create mode 100644 vendor/valuable/src/tuplable.rs create mode 100644 vendor/valuable/src/valuable.rs create mode 100644 vendor/valuable/src/value.rs create mode 100644 vendor/valuable/src/visit.rs create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/.cargo-checksum.json create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/CODE_OF_CONDUCT.md create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/CONTRIBUTING.md create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/Cargo.toml rename vendor/{object-0.27.1 => wasi-0.9.0+wasi-snapshot-preview1}/LICENSE-APACHE (100%) create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/LICENSE-Apache-2.0_WITH_LLVM-exception create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/LICENSE-MIT create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/ORG_CODE_OF_CONDUCT.md create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/README.md create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/SECURITY.md rename vendor/{wasi => wasi-0.9.0+wasi-snapshot-preview1}/old-bitflags.patch (100%) rename vendor/{wasi => wasi-0.9.0+wasi-snapshot-preview1}/src/error.rs (100%) create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/src/lib.rs create mode 100644 vendor/wasi-0.9.0+wasi-snapshot-preview1/src/lib_generated.rs diff --git a/Cargo.lock b/Cargo.lock index 51d937d4dc..e3ab987b3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,17 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "ahash" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +dependencies = [ + "getrandom 0.2.0", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -45,9 +56,9 @@ dependencies = [ [[package]] name = "ammonia" -version = "3.1.0" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89eac85170f4b3fb3dc5e442c1cfb036cb8eecf9dbbd431a161ffad15d90ea3b" +checksum = "b74b175af97d1aecc1add0878b1cbfcbf3bd4c22d7713eeb6d597da23e29bc0d" dependencies = [ "html5ever", "lazy_static", @@ -67,15 +78,6 @@ dependencies = [ "yansi-term", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -103,6 +105,49 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7" +[[package]] +name = "askama" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d8f355701c672c2ba3d718acbd213f740beea577cc4eae66accdffe15be1882" +dependencies = [ + "askama_derive", + "askama_escape", + "askama_shared", +] + +[[package]] +name = "askama_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84704cab5b7ae0fd3a9f78ee5eb7b27f3749df445f04623db6633459ae283267" +dependencies = [ + "askama_shared", + "proc-macro2", + "syn", +] + +[[package]] +name = "askama_escape" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1bb320f97e6edf9f756bf015900038e43c7700e059688e5724a928c8f3b8d5" + +[[package]] +name = "askama_shared" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae03eebba55a2697a376e58b573a29fe36893157173ac8df312ad85f3c0e012" +dependencies = [ + "askama_escape", + "nom", + "proc-macro2", + "quote", + "serde", + "syn", + "toml", +] + [[package]] name = "atty" version = "0.2.14" @@ -149,9 +194,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ "generic-array 0.14.4", ] @@ -182,7 +227,6 @@ dependencies = [ "pretty_assertions", "serde", "serde_json", - "time", "toml", "winapi", ] @@ -274,7 +318,7 @@ dependencies = [ [[package]] name = "cargo" -version = "0.60.0" +version = "0.61.0" dependencies = [ "anyhow", "atty", @@ -283,9 +327,9 @@ dependencies = [ "cargo-test-macro", "cargo-test-support", "cargo-util", - "clap", + "clap 3.1.5", "crates-io", - "crossbeam-utils 0.8.3", + "crossbeam-utils", "curl", "curl-sys", "env_logger 0.9.0", @@ -316,7 +360,7 @@ dependencies = [ "pretty_env_logger", "rustc-workspace-hack", "rustfix 0.6.0", - "semver 1.0.3", + "semver", "serde", "serde_ignored", "serde_json", @@ -325,7 +369,7 @@ dependencies = [ "tar", "tempfile", "termcolor", - "toml", + "toml_edit", "unicode-width", "unicode-xid", "url 2.2.2", @@ -411,7 +455,7 @@ dependencies = [ "serde_json", "tar", "termcolor", - "toml", + "toml_edit", "url 2.2.2", ] @@ -435,17 +479,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cargo_metadata" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345" -dependencies = [ - "semver 0.11.0", - "serde", - "serde_json", -] - [[package]] name = "cargo_metadata" version = "0.14.0" @@ -454,7 +487,7 @@ checksum = "c297bd3135f558552f99a0daa180876984ea2c4ffa7470314540dff8c654109a" dependencies = [ "camino", "cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 1.0.3", + "semver", "serde", "serde_json", ] @@ -490,9 +523,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54e3b5f9e3425e6b119ff07568d8d006bfa5a8d6f78a9cbc3530b1e962e316c" +checksum = "58c24b8052ea1e3adbb6f9ab7ba5fcc18b9d12591c042de4c833f709ce81e0e0" dependencies = [ "proc-macro2", "quote", @@ -502,9 +535,9 @@ dependencies = [ [[package]] name = "chalk-engine" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdc891073396b167163db77123b0a3c00088edc00466cecc5531f33e3e989523" +checksum = "0eca186b6ea9af798312f4b568fd094c82e7946ac08be5dc5fea22decc6d2ed8" dependencies = [ "chalk-derive", "chalk-ir", @@ -515,9 +548,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b79e5a1d04b79311e90c69356a2c62027853906a7e33b3e070b93c055fc3e8a" +checksum = "f3cad5c3f1edd4b4a2c9bda24ae558ceb4f88336f88f944c2e35d0bfeb13c818" dependencies = [ "bitflags", "chalk-derive", @@ -526,13 +559,14 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.75.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d2a1db6605aba70a58820bd80ac422b218913a510f1a40beef9efc5371ea1d" +checksum = "94533188d3452bc72cbd5618d166f45fc7646b674ad3fe9667d557bc25236dee" dependencies = [ "chalk-derive", "chalk-ir", "ena", + "indexmap", "itertools 0.10.1", "petgraph", "rustc-hash", @@ -560,26 +594,42 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "atty", "bitflags", - "strsim", - "textwrap", + "strsim 0.8.0", + "textwrap 0.11.0", "unicode-width", "vec_map", "yaml-rust 0.3.5", ] +[[package]] +name = "clap" +version = "3.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced1892c55c910c1219e98d6fc8d71f6bddba7905866ce740066d8bfea859312" +dependencies = [ + "atty", + "bitflags", + "indexmap", + "os_str_bytes", + "strsim 0.10.0", + "termcolor", + "textwrap 0.15.0", +] + [[package]] name = "clippy" -version = "0.1.59" +version = "0.1.60" dependencies = [ - "cargo_metadata 0.14.0", + "cargo_metadata", "clippy_lints", "clippy_utils", "compiletest_rs", "derive-new", "filetime", + "futures 0.3.19", "if_chain", "itertools 0.10.1", "parking_lot", @@ -587,11 +637,12 @@ dependencies = [ "regex", "rustc-workspace-hack", "rustc_tools_util 0.2.0", - "semver 1.0.3", + "semver", "serde", "syn", "tempfile", "tester", + "tokio", ] [[package]] @@ -599,8 +650,8 @@ name = "clippy_dev" version = "0.0.1" dependencies = [ "bytecount", - "cargo_metadata 0.14.0", - "clap", + "cargo_metadata", + "clap 2.34.0", "indoc", "itertools 0.10.1", "opener", @@ -611,9 +662,9 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.59" +version = "0.1.60" dependencies = [ - "cargo_metadata 0.14.0", + "cargo_metadata", "clippy_utils", "if_chain", "itertools 0.10.1", @@ -621,7 +672,7 @@ dependencies = [ "quine-mc_cluskey", "regex-syntax", "rustc-semver", - "semver 1.0.3", + "semver", "serde", "serde_json", "toml", @@ -632,8 +683,9 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.59" +version = "0.1.60" dependencies = [ + "arrayvec", "if_chain", "rustc-semver", ] @@ -658,6 +710,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "combine" +version = "4.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "commoncrypto" version = "0.2.0" @@ -678,9 +740,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.67" +version = "0.1.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68c69e9451f1df4b215c9588c621670c12286b53e60fb5ec4b59aaa1138d18e" +checksum = "80873f979f0a344a4ade87c2f70d9ccf5720b83b10c97ec7cd745895d021e85a" dependencies = [ "cc", "rustc-std-workspace-core", @@ -759,14 +821,17 @@ name = "coverage_test_macros" version = "0.0.0" [[package]] -name = "cpuid-bool" -version = "0.1.2" +name = "cpufeatures" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] [[package]] name = "crates-io" -version = "0.33.1" +version = "0.34.0" dependencies = [ "anyhow", "curl", @@ -787,71 +852,55 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.3", + "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ + "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.7.2", - "maybe-uninit", + "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.8.2" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", + "cfg-if 1.0.0", + "crossbeam-utils", "lazy_static", - "maybe-uninit", "memoffset", "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -dependencies = [ - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - [[package]] name = "crossbeam-utils" -version = "0.7.2" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" dependencies = [ - "autocfg", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "lazy_static", ] [[package]] -name = "crossbeam-utils" -version = "0.8.3" +name = "crypto-common" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" dependencies = [ - "autocfg", - "cfg-if 1.0.0", - "lazy_static", + "generic-array 0.14.4", ] [[package]] @@ -968,11 +1017,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" dependencies = [ - "generic-array 0.14.4", + "block-buffer 0.10.2", + "crypto-common", ] [[package]] @@ -1266,15 +1316,15 @@ dependencies = [ [[package]] name = "futures" -version = "0.1.29" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" +checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" dependencies = [ "futures-channel", "futures-core", @@ -1287,9 +1337,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" dependencies = [ "futures-core", "futures-sink", @@ -1297,15 +1347,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" [[package]] name = "futures-executor" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" +checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" dependencies = [ "futures-core", "futures-task", @@ -1315,17 +1365,16 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" +checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" [[package]] name = "futures-macro" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" dependencies = [ - "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -1333,26 +1382,23 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" [[package]] name = "futures-task" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" -dependencies = [ - "once_cell", -] +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" [[package]] name = "futures-util" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" dependencies = [ - "futures 0.1.29", + "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -1362,8 +1408,6 @@ dependencies = [ "memchr", "pin-project-lite", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] @@ -1415,7 +1459,7 @@ checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ "cfg-if 0.1.10", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -1426,7 +1470,7 @@ checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4" dependencies = [ "cfg-if 0.1.10", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -1509,17 +1553,6 @@ dependencies = [ "regex", ] -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags", - "ignore", - "walkdir", -] - [[package]] name = "gsgdt" version = "0.1.2" @@ -1545,9 +1578,18 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.0" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "362385356d610bd1e5a408ddf8d022041774b683f345a1d2cfcb4f60f8ae2db5" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -1665,7 +1707,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c" dependencies = [ - "crossbeam-utils 0.8.3", + "crossbeam-utils", "globset", "lazy_static", "log", @@ -1693,12 +1735,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", + "rustc-rayon", "serde", ] @@ -1716,7 +1759,7 @@ name = "installer" version = "0.0.0" dependencies = [ "anyhow", - "clap", + "clap 2.34.0", "flate2", "lazy_static", "num_cpus", @@ -1809,7 +1852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a" dependencies = [ "derive_more", - "futures 0.3.12", + "futures 0.3.19", "jsonrpc-core", "jsonrpc-pubsub", "jsonrpc-server-utils", @@ -1827,7 +1870,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "futures 0.3.12", + "futures 0.3.19", "futures-executor", "futures-util", "log", @@ -1842,7 +1885,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0" dependencies = [ - "futures 0.3.12", + "futures 0.3.19", "jsonrpc-client-transports", ] @@ -1864,7 +1907,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "382bb0206323ca7cda3dcd7e245cea86d37d02457a02a975e3378fb149a48845" dependencies = [ - "futures 0.3.12", + "futures 0.3.19", "jsonrpc-core", "jsonrpc-server-utils", "log", @@ -1879,7 +1922,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011" dependencies = [ - "futures 0.3.12", + "futures 0.3.19", "jsonrpc-core", "lazy_static", "log", @@ -1895,7 +1938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" dependencies = [ "bytes", - "futures 0.3.12", + "futures 0.3.19", "globset", "jsonrpc-core", "lazy_static", @@ -1906,6 +1949,15 @@ dependencies = [ "unicase", ] +[[package]] +name = "kstring" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b310ccceade8121d7d77fee406160e457c2f4e7c7982d589da3499bc7ea4526" +dependencies = [ + "serde", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1920,9 +1972,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.108" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" +checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74" dependencies = [ "rustc-std-workspace-core", ] @@ -2003,9 +2055,9 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lint-docs" @@ -2093,16 +2145,13 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "markup5ever" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab" +checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" dependencies = [ "log", "phf", "phf_codegen", - "serde", - "serde_derive", - "serde_json", "string_cache", "string_cache_codegen", "tendril", @@ -2135,21 +2184,13 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "md-5" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +checksum = "e6a38fc55c8bbc10058782919516f88826e70320db6d206aebc49611d24216ae" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest 0.10.2", ] [[package]] @@ -2161,7 +2202,7 @@ dependencies = [ "ammonia", "anyhow", "chrono", - "clap", + "clap 2.34.0", "elasticlunr-rs", "env_logger 0.7.1", "handlebars", @@ -2229,22 +2270,28 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.5.5" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] [[package]] name = "minifier" -version = "0.0.41" +version = "0.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5594542d20834f2b974f5e5fb8e0cf1c67a2119dcadc29ef5d93a081fb30cc08" +checksum = "55a1388517eda8a68875243b650c26997e055a33d82571b5a0349129faef7d99" dependencies = [ "macro-utils", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.4.0" @@ -2259,9 +2306,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", @@ -2304,6 +2351,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -2334,9 +2392,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", @@ -2349,8 +2407,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2" dependencies = [ "compiler_builtins", - "crc32fast", - "indexmap", "memchr", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -2358,12 +2414,13 @@ dependencies = [ [[package]] name = "object" -version = "0.27.1" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +checksum = "7ce8b38d41f9f3618fc23f908faae61510f8d8ce2d99cbe910641e8f1971f084" dependencies = [ "crc32fast", "flate2", + "hashbrown 0.11.2", "indexmap", "memchr", ] @@ -2389,12 +2446,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "opener" version = "0.5.0" @@ -2427,9 +2478,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-src" -version = "111.16.0+1.1.1l" +version = "111.17.0+1.1.1m" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab2173f69416cf3ec12debb5823d244127d23a9b127d5a5189aa97c5fa2859f" +checksum = "05d6a336abd10814198f66e2a91ccd7336611f30334119ca8ce300536666fcf4" dependencies = [ "cc", ] @@ -2465,6 +2516,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + [[package]] name = "output_vt100" version = "0.1.2" @@ -2513,7 +2573,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" dependencies = [ - "futures 0.3.12", + "futures 0.3.19", "libc", "log", "rand 0.7.3", @@ -2666,9 +2726,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.4" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" [[package]] name = "pin-utils" @@ -2707,13 +2767,13 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_assertions" -version = "0.6.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" +checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b" dependencies = [ - "ansi_term 0.11.0", + "ansi_term", "ctor", - "difference", + "diff", "output_vt100", ] @@ -2760,18 +2820,6 @@ 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.30" @@ -2853,25 +2901,18 @@ dependencies = [ [[package]] name = "racer" -version = "2.1.48" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fec2e85e7a30f8fd31b7cf288ad363b5e51fd2cb6f53b416b0cfaabd84e1ccb" +checksum = "b0b4b5faaf07040474e8af74a9e19ff167d5d204df5db5c5c765edecfb900358" dependencies = [ "bitflags", - "clap", + "clap 2.34.0", "derive_more", "env_logger 0.7.1", "humantime 2.0.1", "lazy_static", "log", "rls-span", - "rustc-ap-rustc_ast", - "rustc-ap-rustc_ast_pretty", - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_errors", - "rustc-ap-rustc_parse", - "rustc-ap-rustc_session", - "rustc-ap-rustc_span", ] [[package]] @@ -2994,9 +3035,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.3.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg", "crossbeam-deque", @@ -3006,13 +3047,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.7.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ + "crossbeam-channel", "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils 0.7.2", + "crossbeam-utils", "lazy_static", "num_cpus", ] @@ -3086,12 +3127,12 @@ dependencies = [ "anyhow", "cargo", "cargo-util", - "cargo_metadata 0.14.0", + "cargo_metadata", "clippy_lints", "crossbeam-channel", "difference", "env_logger 0.9.0", - "futures 0.3.12", + "futures 0.3.19", "heck", "home", "itertools 0.10.1", @@ -3124,18 +3165,19 @@ dependencies = [ "tokio-stream", "tokio-util", "toml", + "toml_edit", "url 2.2.2", "walkdir", ] [[package]] name = "rls-analysis" -version = "0.18.2" +version = "0.18.3" dependencies = [ "derive-new", - "env_logger 0.7.1", + "env_logger 0.9.0", "fst", - "itertools 0.9.0", + "itertools 0.10.1", "json", "lazy_static", "log", @@ -3172,10 +3214,10 @@ name = "rls-rustc" version = "0.6.0" dependencies = [ "clippy_lints", - "env_logger 0.7.1", - "futures 0.3.12", + "env_logger 0.9.0", + "futures 0.3.19", "log", - "rand 0.7.3", + "rand 0.8.4", "rls-data", "rls-ipc", "serde", @@ -3213,257 +3255,11 @@ dependencies = [ name = "rustbook" version = "0.1.0" dependencies = [ - "clap", + "clap 2.34.0", "env_logger 0.7.1", "mdbook", ] -[[package]] -name = "rustc-ap-rustc_arena" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "550ca1a0925d31a0af089b18c89f5adf3b286e319e3e1f1a5204c21bd2f17371" -dependencies = [ - "rustc-ap-rustc_data_structures", - "smallvec", -] - -[[package]] -name = "rustc-ap-rustc_ast" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa53b68080df17994a54747f7c37b0686288a670efb9ba3b382ce62e744aed2" -dependencies = [ - "bitflags", - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_index", - "rustc-ap-rustc_lexer", - "rustc-ap-rustc_macros", - "rustc-ap-rustc_serialize", - "rustc-ap-rustc_span", - "smallvec", - "tracing", -] - -[[package]] -name = "rustc-ap-rustc_ast_pretty" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae71e68fada466a4b2c39c79ca6aee3226587abe6787170d2f6c92237569565" -dependencies = [ - "rustc-ap-rustc_ast", - "rustc-ap-rustc_span", - "tracing", -] - -[[package]] -name = "rustc-ap-rustc_data_structures" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa484d6e0ca32d1d82303647275c696f745599b3d97e686f396ceef5b99d7ae" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if 0.1.10", - "crossbeam-utils 0.8.3", - "ena", - "indexmap", - "jobserver", - "libc", - "measureme 9.1.2", - "memmap2", - "parking_lot", - "rustc-ap-rustc_graphviz", - "rustc-ap-rustc_index", - "rustc-ap-rustc_macros", - "rustc-ap-rustc_serialize", - "rustc-hash", - "rustc-rayon", - "rustc-rayon-core", - "smallvec", - "stable_deref_trait", - "stacker", - "tempfile", - "tracing", - "winapi", -] - -[[package]] -name = "rustc-ap-rustc_errors" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f85ba19cca320ad797e3a29c35cab9bddfff0e7adbde336a436249e54cee7b1" -dependencies = [ - "annotate-snippets", - "atty", - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_lint_defs", - "rustc-ap-rustc_macros", - "rustc-ap-rustc_serialize", - "rustc-ap-rustc_span", - "termcolor", - "termize", - "tracing", - "unicode-width", - "winapi", -] - -[[package]] -name = "rustc-ap-rustc_feature" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d538adab96b8b2b1ca9fcd4c8c47d4e23e862a23d1a38b6c15cd8fd52b34b1" -dependencies = [ - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_span", -] - -[[package]] -name = "rustc-ap-rustc_fs_util" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad6f13d240944fa8f360d2f3b849a7cadaec75e477829e7dde61e838deda83d" - -[[package]] -name = "rustc-ap-rustc_graphviz" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08b3451153cc5828c02cc4f1a0df146d25ac4b3382a112e25fd9d3f5bff15cdc" - -[[package]] -name = "rustc-ap-rustc_index" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd39a9f01b442c629bdff5778cb3dd29b7c2ea4afe62d5ab61d216bd1b556692" -dependencies = [ - "arrayvec", - "rustc-ap-rustc_macros", - "rustc-ap-rustc_serialize", -] - -[[package]] -name = "rustc-ap-rustc_lexer" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5de290c44a90e671d2cd730062b9ef73d11155da7e44e7741d633e1e51e616e" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "rustc-ap-rustc_lint_defs" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69570b4beb61088926b131579865bbe70d124d30778c46307a62ec8b310ae462" -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 = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bd877df37f15c5a44d9679d1b5207ebc95f3943fbc336eeac670195ac58610" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "rustc-ap-rustc_parse" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02502d8522ba31d0bcad28a78822b68c1b6ba947a2b4aa6a2341b30594379b80" -dependencies = [ - "bitflags", - "rustc-ap-rustc_ast", - "rustc-ap-rustc_ast_pretty", - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_errors", - "rustc-ap-rustc_feature", - "rustc-ap-rustc_lexer", - "rustc-ap-rustc_session", - "rustc-ap-rustc_span", - "smallvec", - "tracing", - "unicode-normalization", -] - -[[package]] -name = "rustc-ap-rustc_serialize" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f741f8e9aee6323fbe127329490608a5a250cc0072ac91e684ef62518cdb1ff" -dependencies = [ - "indexmap", - "smallvec", -] - -[[package]] -name = "rustc-ap-rustc_session" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba61eca749f4fced4427ad1cc7f23342cfc6527c3bcc624e3aa56abc1f81298" -dependencies = [ - "bitflags", - "getopts", - "num_cpus", - "rustc-ap-rustc_ast", - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_errors", - "rustc-ap-rustc_feature", - "rustc-ap-rustc_fs_util", - "rustc-ap-rustc_lint_defs", - "rustc-ap-rustc_macros", - "rustc-ap-rustc_serialize", - "rustc-ap-rustc_span", - "rustc-ap-rustc_target", - "tracing", -] - -[[package]] -name = "rustc-ap-rustc_span" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a642e8d6fc883f34e0778e079f8242ac40c6614a6b7a0ef61681333e847f5e62" -dependencies = [ - "cfg-if 0.1.10", - "md-5", - "rustc-ap-rustc_arena", - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_index", - "rustc-ap-rustc_macros", - "rustc-ap-rustc_serialize", - "scoped-tls", - "sha-1 0.9.1", - "sha2", - "tracing", - "unicode-width", -] - -[[package]] -name = "rustc-ap-rustc_target" -version = "722.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80feebd8c323b80dd73a395fa7fabba9e2098b6277670ff89c473f618ffa07de" -dependencies = [ - "bitflags", - "rustc-ap-rustc_data_structures", - "rustc-ap-rustc_index", - "rustc-ap-rustc_macros", - "rustc-ap-rustc_serialize", - "rustc-ap-rustc_span", - "tracing", -] - [[package]] name = "rustc-demangle" version = "0.1.21" @@ -3487,14 +3283,13 @@ dependencies = [ "rustc_codegen_ssa", "rustc_driver", "tikv-jemalloc-sys", - "tikv-jemallocator", ] [[package]] name = "rustc-rayon" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7d6a39f8bfd4421ce720918234d1e672b83824c91345b47c93746839cf1629" +checksum = "9974ab223660e61c1b4e7b43b827379df286736ca988308ce7e16f59f2d89246" dependencies = [ "crossbeam-deque", "either", @@ -3503,13 +3298,12 @@ dependencies = [ [[package]] name = "rustc-rayon-core" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94187d9ea3e8c38fafdbc38acb94eafa7ce155867f6ccb13830466a0d0db8c6" +checksum = "564bfd27be8db888d0fa76aa4335e7851aaed0c2c11ad1e93aeb9349f6b88500" dependencies = [ "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils 0.7.2", + "crossbeam-utils", "lazy_static", "num_cpus", ] @@ -3547,7 +3341,7 @@ version = "1.0.0" dependencies = [ "bstr", "byteorder", - "crossbeam-utils 0.8.3", + "crossbeam-utils", "libc", "libz-sys", "proc-macro2", @@ -3573,7 +3367,6 @@ dependencies = [ name = "rustc_arena" version = "0.0.0" dependencies = [ - "rustc_data_structures", "smallvec", ] @@ -3615,7 +3408,7 @@ dependencies = [ name = "rustc_ast_passes" version = "0.0.0" dependencies = [ - "itertools 0.9.0", + "itertools 0.10.1", "rustc_ast", "rustc_ast_pretty", "rustc_attr", @@ -3635,7 +3428,6 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_span", - "tracing", ] [[package]] @@ -3659,7 +3451,7 @@ name = "rustc_borrowck" version = "0.0.0" dependencies = [ "either", - "itertools 0.9.0", + "itertools 0.10.1", "polonius-engine", "rustc_const_eval", "rustc_data_structures", @@ -3740,10 +3532,10 @@ version = "0.0.0" dependencies = [ "bitflags", "cc", - "itertools 0.9.0", + "itertools 0.10.1", "jobserver", "libc", - "object 0.26.2", + "object 0.28.1", "pathdiff", "regex", "rustc_apfloat", @@ -3948,7 +3740,7 @@ dependencies = [ name = "rustc_incremental" version = "0.0.0" dependencies = [ - "rand 0.7.3", + "rand 0.8.4", "rustc_ast", "rustc_data_structures", "rustc_errors", @@ -4224,7 +4016,7 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "coverage_test_macros", - "itertools 0.9.0", + "itertools 0.10.1", "rustc_ast", "rustc_attr", "rustc_const_eval", @@ -4466,7 +4258,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "scoped-tls", - "sha-1 0.9.1", + "sha-1 0.10.0", "sha2", "tracing", "unicode-width", @@ -4590,6 +4382,7 @@ dependencies = [ "rustc_attr", "rustc_data_structures", "rustc_errors", + "rustc_graphviz", "rustc_hir", "rustc_hir_pretty", "rustc_index", @@ -4597,6 +4390,7 @@ dependencies = [ "rustc_lint", "rustc_macros", "rustc_middle", + "rustc_serialize", "rustc_session", "rustc_span", "rustc_target", @@ -4612,7 +4406,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.3", + "semver", ] [[package]] @@ -4620,8 +4414,10 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", + "askama", + "atty", "expect-test", - "itertools 0.9.0", + "itertools 0.10.1", "minifier", "pulldown-cmark", "rayon", @@ -4631,7 +4427,6 @@ dependencies = [ "serde_json", "smallvec", "tempfile", - "tera", "tracing", "tracing-subscriber", "tracing-tree", @@ -4697,7 +4492,7 @@ dependencies = [ "annotate-snippets", "anyhow", "bytecount", - "cargo_metadata 0.14.0", + "cargo_metadata", "derive-new", "diff", "dirs", @@ -4787,16 +4582,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", - "serde", -] - [[package]] name = "semver" version = "1.0.3" @@ -4806,15 +4591,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "1.0.125" @@ -4876,33 +4652,29 @@ dependencies = [ "block-buffer 0.7.3", "digest 0.8.1", "fake-simd", - "opaque-debug 0.2.3", + "opaque-debug", ] [[package]] name = "sha-1" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ - "block-buffer 0.9.0", - "cfg-if 0.1.10", - "cpuid-bool", - "digest 0.9.0", - "opaque-debug 0.3.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.2", ] [[package]] name = "sha2" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" dependencies = [ - "block-buffer 0.9.0", - "cfg-if 0.1.10", - "cpuid-bool", - "digest 0.9.0", - "opaque-debug 0.3.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.2", ] [[package]] @@ -5009,7 +4781,7 @@ dependencies = [ "core", "dlmalloc", "fortanix-sgx-abi", - "hashbrown", + "hashbrown 0.12.0", "hermit-abi", "libc", "miniz_oxide", @@ -5021,7 +4793,7 @@ dependencies = [ "rustc-demangle", "std_detect", "unwind", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -5075,13 +4847,19 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "structopt" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" dependencies = [ - "clap", + "clap 2.34.0", "lazy_static", "structopt-derive", ] @@ -5176,21 +4954,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "tera" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81060acb882480c8793782eb96bc86f5c83d2fc7175ad46c375c6956ef7afa62" -dependencies = [ - "globwalk", - "lazy_static", - "pest", - "pest_derive", - "regex", - "serde", - "serde_json", -] - [[package]] name = "term" version = "0.6.1" @@ -5267,6 +5030,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + [[package]] name = "thiserror" version = "1.0.30" @@ -5289,31 +5058,31 @@ dependencies = [ [[package]] name = "thorin-dwp" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039d1fc0bfdb73910c2702893515580e38c192f47a987bc98ddd38a36f2d953a" +checksum = "dd95b4559c196987c8451b4e14d08a4c796c2844f9adf4d2a2dbc9b3142843be" dependencies = [ "gimli 0.26.1", - "indexmap", - "object 0.27.1", + "hashbrown 0.11.2", + "object 0.28.1", "tracing", ] [[package]] name = "thread_local" -version = "1.0.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] name = "tidy" version = "0.1.0" dependencies = [ - "cargo_metadata 0.12.0", - "crossbeam-utils 0.8.3", + "cargo_metadata", + "crossbeam-utils", "lazy_static", "regex", "walkdir", @@ -5334,16 +5103,6 @@ dependencies = [ "libc", ] -[[package]] -name = "tikv-jemallocator" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c14a5a604eb8715bc5785018a37d00739b180bcf609916ddf4393d33d49ccdf" -dependencies = [ - "libc", - "tikv-jemalloc-sys", -] - [[package]] name = "time" version = "0.1.43" @@ -5362,9 +5121,9 @@ checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" [[package]] name = "tokio" -version = "1.8.2" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2602b8af3767c285202012822834005f596c811042315fa7e9f5b12b2a43207" +checksum = "50dae83881bc9b0403dd5b44ea9deed3e939856cc8722d5be37f0d6e5c6d53dd" dependencies = [ "autocfg", "bytes", @@ -5412,6 +5171,19 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744e9ed5b352340aa47ce033716991b5589e23781acb97cad37d4ea70560f55b" +dependencies = [ + "combine", + "indexmap", + "itertools 0.10.1", + "kstring", + "serde", +] + [[package]] name = "topological-sort" version = "0.1.0" @@ -5473,7 +5245,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "lazy_static", "matchers", "parking_lot", @@ -5492,7 +5264,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ce989c9962c7f61fe084dd4a230eec784649dfc2392467c790007c3a6e134e7" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "atty", "tracing-core", "tracing-log", @@ -5769,6 +5541,12 @@ name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", diff --git a/RELEASES.md b/RELEASES.md index 37b41d2642..0965e37574 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,10 +1,165 @@ +Version 1.60.0 (2022-04-07) +========================== + +Language +-------- +- [Stabilize `#[cfg(panic = "...")]` for either `"unwind"` or `"abort"`.][93658] +- [Stabilize `#[cfg(target_has_atomic = "...")]` for each integer size and `"ptr"`.][93824] + +Compiler +-------- +- [Enable combining `+crt-static` and `relocation-model=pic` on `x86_64-unknown-linux-gnu`][86374] +- [Fixes wrong `unreachable_pub` lints on nested and glob public reexport][87487] +- [Stabilize `-Z instrument-coverage` as `-C instrument-coverage`][90132] +- [Stabilize `-Z print-link-args` as `--print link-args`][91606] +- [Add new Tier 3 target `mips64-openwrt-linux-musl`\*][92300] +- [Add new Tier 3 target `armv7-unknown-linux-uclibceabi` (softfloat)\*][92383] +- [Fix invalid removal of newlines from doc comments][92357] +- [Add kernel target for RustyHermit][92670] +- [Deny mixing bin crate type with lib crate types][92933] +- [Make rustc use `RUST_BACKTRACE=full` by default][93566] +- [Upgrade to LLVM 14][93577] + +\* Refer to Rust's [platform support page][platform-support-doc] for more + information on Rust's tiered platform support. + +Libraries +--------- +- [Guarantee call order for `sort_by_cached_key`][89621] +- [Improve `Duration::try_from_secs_f32`/`f64` accuracy by directly processing exponent and mantissa][90247] +- [Make `Instant::{duration_since, elapsed, sub}` saturating][89926] +- [Remove non-monotonic clocks workarounds in `Instant::now`][89926] +- [Make `BuildHasherDefault`, `iter::Empty` and `future::Pending` covariant][92630] + +Stabilized APIs +--------------- +- [`Arc::new_cyclic`][arc_new_cyclic] +- [`Rc::new_cyclic`][rc_new_cyclic] +- [`slice::EscapeAscii`][slice_escape_ascii] +- [`<[u8]>::escape_ascii`][slice_u8_escape_ascii] +- [`u8::escape_ascii`][u8_escape_ascii] +- [`Vec::spare_capacity_mut`][vec_spare_capacity_mut] +- [`MaybeUninit::assume_init_drop`][assume_init_drop] +- [`MaybeUninit::assume_init_read`][assume_init_read] +- [`i8::abs_diff`][i8_abs_diff] +- [`i16::abs_diff`][i16_abs_diff] +- [`i32::abs_diff`][i32_abs_diff] +- [`i64::abs_diff`][i64_abs_diff] +- [`i128::abs_diff`][i128_abs_diff] +- [`isize::abs_diff`][isize_abs_diff] +- [`u8::abs_diff`][u8_abs_diff] +- [`u16::abs_diff`][u16_abs_diff] +- [`u32::abs_diff`][u32_abs_diff] +- [`u64::abs_diff`][u64_abs_diff] +- [`u128::abs_diff`][u128_abs_diff] +- [`usize::abs_diff`][usize_abs_diff] +- [`Display for io::ErrorKind`][display_error_kind] +- [`From for ExitCode`][from_u8_exit_code] +- [`Not for !` (the "never" type)][not_never] +- [_Op_`Assign<$t> for Wrapping<$t>`][wrapping_assign_ops] +- [`arch::is_aarch64_feature_detected!`][is_aarch64_feature_detected] + +Cargo +----- +- [Port cargo from `toml-rs` to `toml_edit`][cargo/10086] +- [Stabilize `-Ztimings` as `--timings`][cargo/10245] +- [Stabilize namespaced and weak dependency features.][cargo/10269] +- [Accept more `cargo:rustc-link-arg-*` types from build script output.][cargo/10274] +- [cargo-new should not add ignore rule on Cargo.lock inside subdirs][cargo/10379] + +Misc +---- +- [Ship docs on Tier 2 platforms by reusing the closest Tier 1 platform docs][92800] +- [Drop rustc-docs from complete profile][93742] +- [bootstrap: tidy up flag handling for llvm build][93918] + +Compatibility Notes +------------------- +- [Remove compiler-rt linking hack on Android][83822] +- [Mitigations for platforms with non-monotonic clocks have been removed from + `Instant::now`][89926]. On platforms that don't provide monotonic clocks, an + instant is not guaranteed to be greater than an earlier instant anymore. +- [`Instant::{duration_since, elapsed, sub}` do not panic anymore on underflow, + saturating to `0` instead][89926]. In the real world the panic happened mostly + on platforms with buggy monotonic clock implementations rather than catching + programming errors like reversing the start and end times. Such programming + errors will now results in `0` rather than a panic. +- In a future release we're planning to increase the baseline requirements for + the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love + your feedback in [PR #95026][95026]. + +Internal Changes +---------------- + +These changes provide no direct user facing benefits, but represent significant +improvements to the internals and overall performance of rustc +and related tools. + +- [Switch all libraries to the 2021 edition][92068] + +[83822]: https://github.com/rust-lang/rust/pull/83822 +[86374]: https://github.com/rust-lang/rust/pull/86374 +[87487]: https://github.com/rust-lang/rust/pull/87487 +[89621]: https://github.com/rust-lang/rust/pull/89621 +[89926]: https://github.com/rust-lang/rust/pull/89926 +[90132]: https://github.com/rust-lang/rust/pull/90132 +[90247]: https://github.com/rust-lang/rust/pull/90247 +[91606]: https://github.com/rust-lang/rust/pull/91606 +[92068]: https://github.com/rust-lang/rust/pull/92068 +[92300]: https://github.com/rust-lang/rust/pull/92300 +[92357]: https://github.com/rust-lang/rust/pull/92357 +[92383]: https://github.com/rust-lang/rust/pull/92383 +[92630]: https://github.com/rust-lang/rust/pull/92630 +[92670]: https://github.com/rust-lang/rust/pull/92670 +[92800]: https://github.com/rust-lang/rust/pull/92800 +[92933]: https://github.com/rust-lang/rust/pull/92933 +[93566]: https://github.com/rust-lang/rust/pull/93566 +[93577]: https://github.com/rust-lang/rust/pull/93577 +[93658]: https://github.com/rust-lang/rust/pull/93658 +[93742]: https://github.com/rust-lang/rust/pull/93742 +[93824]: https://github.com/rust-lang/rust/pull/93824 +[93918]: https://github.com/rust-lang/rust/pull/93918 +[95026]: https://github.com/rust-lang/rust/pull/95026 + +[cargo/10086]: https://github.com/rust-lang/cargo/pull/10086 +[cargo/10245]: https://github.com/rust-lang/cargo/pull/10245 +[cargo/10269]: https://github.com/rust-lang/cargo/pull/10269 +[cargo/10274]: https://github.com/rust-lang/cargo/pull/10274 +[cargo/10379]: https://github.com/rust-lang/cargo/pull/10379 + +[arc_new_cyclic]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_cyclic +[rc_new_cyclic]: https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_cyclic +[slice_escape_ascii]: https://doc.rust-lang.org/stable/std/slice/struct.EscapeAscii.html +[slice_u8_escape_ascii]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.escape_ascii +[u8_escape_ascii]: https://doc.rust-lang.org/stable/std/primitive.u8.html#method.escape_ascii +[vec_spare_capacity_mut]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.spare_capacity_mut +[assume_init_drop]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init_drop +[assume_init_read]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init_read +[i8_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i8.html#method.abs_diff +[i16_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i16.html#method.abs_diff +[i32_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i32.html#method.abs_diff +[i64_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i64.html#method.abs_diff +[i128_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i128.html#method.abs_diff +[isize_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.isize.html#method.abs_diff +[u8_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u8.html#method.abs_diff +[u16_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u16.html#method.abs_diff +[u32_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u32.html#method.abs_diff +[u64_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u64.html#method.abs_diff +[u128_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u128.html#method.abs_diff +[usize_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.usize.html#method.abs_diff +[display_error_kind]: https://doc.rust-lang.org/stable/std/io/enum.ErrorKind.html#impl-Display +[from_u8_exit_code]: https://doc.rust-lang.org/stable/std/process/struct.ExitCode.html#impl-From%3Cu8%3E +[not_never]: https://doc.rust-lang.org/stable/std/primitive.never.html#impl-Not +[wrapping_assign_ops]: https://doc.rust-lang.org/stable/std/num/struct.Wrapping.html#trait-implementations +[is_aarch64_feature_detected]: https://doc.rust-lang.org/stable/std/arch/macro.is_aarch64_feature_detected.html + Version 1.59.0 (2022-02-24) ========================== Language -------- -- [Stabilize default arguments for const generics][90207] +- [Stabilize default arguments for const parameters and remove the ordering restriction for type and const parameters][90207] - [Stabilize destructuring assignment][90521] - [Relax private in public lint on generic bounds and where clauses of trait impls][90586] - [Stabilize asm! and global_asm! for x86, x86_64, ARM, Aarch64, and RISC-V][91728] @@ -58,6 +213,7 @@ Stabilized APIs - [`NonZeroU32::is_power_of_two`][is_power_of_two32] - [`NonZeroU64::is_power_of_two`][is_power_of_two64] - [`NonZeroU128::is_power_of_two`][is_power_of_two128] +- [`NonZeroUsize::is_power_of_two`][is_power_of_two_usize] - [`DoubleEndedIterator for ToLowercase`][lowercase] - [`DoubleEndedIterator for ToUppercase`][uppercase] - [`TryFrom<&mut [T]> for [T; N]`][tryfrom_ref_arr] @@ -143,6 +299,7 @@ and related tools. [91984]: https://github.com/rust-lang/rust/pull/91984/ [92020]: https://github.com/rust-lang/rust/pull/92020/ [92034]: https://github.com/rust-lang/rust/pull/92034/ +[92137]: https://github.com/rust-lang/rust/pull/92137/ [92483]: https://github.com/rust-lang/rust/pull/92483/ [cargo/10088]: https://github.com/rust-lang/cargo/pull/10088/ [cargo/10133]: https://github.com/rust-lang/cargo/pull/10133/ @@ -177,6 +334,7 @@ and related tools. [is_power_of_two32]: https://doc.rust-lang.org/stable/core/num/struct.NonZeroU32.html#method.is_power_of_two [is_power_of_two64]: https://doc.rust-lang.org/stable/core/num/struct.NonZeroU64.html#method.is_power_of_two [is_power_of_two128]: https://doc.rust-lang.org/stable/core/num/struct.NonZeroU128.html#method.is_power_of_two +[is_power_of_two_usize]: https://doc.rust-lang.org/stable/core/num/struct.NonZeroUsize.html#method.is_power_of_two [stdarch/1266]: https://github.com/rust-lang/stdarch/pull/1266 Version 1.58.1 (2022-01-19) @@ -1057,7 +1215,7 @@ Version 1.52.1 (2021-05-10) This release disables incremental compilation, unless the user has explicitly opted in via the newly added RUSTC_FORCE_INCREMENTAL=1 environment variable. -This is due to the widespread, and frequently occuring, breakage encountered by +This is due to the widespread, and frequently occurring, breakage encountered by Rust users due to newly enabled incremental verification in 1.52.0. Notably, Rust users **should** upgrade to 1.52.0 or 1.52.1: the bugs that are detected by newly added incremental verification are still present in past stable versions, diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 277cf0f51d..696c003a58 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -15,11 +15,7 @@ version = '0.4.0' optional = true features = ['unprefixed_malloc_on_supported_platforms'] -[dependencies.tikv-jemallocator] -version = '0.4.0' -optional = true - [features] -jemalloc = ['tikv-jemalloc-sys', 'tikv-jemallocator'] +jemalloc = ['tikv-jemalloc-sys'] llvm = ['rustc_driver/llvm'] max_level_info = ['rustc_driver/max_level_info'] diff --git a/compiler/rustc_arena/Cargo.toml b/compiler/rustc_arena/Cargo.toml index 33ccd04450..ee3a7b51b6 100644 --- a/compiler/rustc_arena/Cargo.toml +++ b/compiler/rustc_arena/Cargo.toml @@ -4,5 +4,4 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_data_structures = { path = "../rustc_data_structures" } smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 6f9ecb9cd2..3928d70c0e 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -45,24 +45,24 @@ pub struct TypedArena { end: Cell<*mut T>, /// A vector of arena chunks. - chunks: RefCell>>, + chunks: RefCell>>, /// Marker indicating that dropping the arena causes its owned /// instances of `T` to be dropped. _own: PhantomData, } -struct TypedArenaChunk { +struct ArenaChunk { /// The raw storage for the arena chunk. storage: Box<[MaybeUninit]>, /// The number of valid entries in the chunk. entries: usize, } -impl TypedArenaChunk { +impl ArenaChunk { #[inline] - unsafe fn new(capacity: usize) -> TypedArenaChunk { - TypedArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 } + unsafe fn new(capacity: usize) -> ArenaChunk { + ArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 } } /// Destroys this arena chunk. @@ -125,6 +125,11 @@ impl IterExt for I where I: IntoIterator, { + // This default collects into a `SmallVec` and then allocates by copying + // from it. The specializations below for types like `Vec` are more + // efficient, copying directly without the intermediate collecting step. + // This default could be made more efficient, like + // `DroplessArena::alloc_from_iter`, but it's not hot enough to bother. #[inline] default fn alloc_from_iter(self, arena: &TypedArena) -> &mut [T] { let vec: SmallVec<[_; 8]> = self.into_iter().collect(); @@ -139,7 +144,7 @@ impl IterExt for std::array::IntoIter { if len == 0 { return &mut []; } - // Move the content to the arena by copying and then forgetting it + // Move the content to the arena by copying and then forgetting it. unsafe { let start_ptr = arena.alloc_raw_slice(len); self.as_slice().as_ptr().copy_to_nonoverlapping(start_ptr, len); @@ -156,7 +161,7 @@ impl IterExt for Vec { if len == 0 { return &mut []; } - // Move the content to the arena by copying and then forgetting it + // Move the content to the arena by copying and then forgetting it. unsafe { let start_ptr = arena.alloc_raw_slice(len); self.as_ptr().copy_to_nonoverlapping(start_ptr, len); @@ -173,7 +178,7 @@ impl IterExt for SmallVec { if len == 0 { return &mut []; } - // Move the content to the arena by copying and then forgetting it + // Move the content to the arena by copying and then forgetting it. unsafe { let start_ptr = arena.alloc_raw_slice(len); self.as_ptr().copy_to_nonoverlapping(start_ptr, len); @@ -272,7 +277,7 @@ impl TypedArena { // Also ensure that this chunk can fit `additional`. new_cap = cmp::max(additional, new_cap); - let mut chunk = TypedArenaChunk::::new(new_cap); + let mut chunk = ArenaChunk::::new(new_cap); self.ptr.set(chunk.start()); self.end.set(chunk.end()); chunks.push(chunk); @@ -281,7 +286,7 @@ impl TypedArena { // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other // chunks. - fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk) { + fn clear_last_chunk(&self, last_chunk: &mut ArenaChunk) { // Determine how much was filled. let start = last_chunk.start() as usize; // We obtain the value of the pointer to the first uninitialized element. @@ -340,7 +345,7 @@ pub struct DroplessArena { end: Cell<*mut u8>, /// A vector of arena chunks. - chunks: RefCell>>, + chunks: RefCell>, } unsafe impl Send for DroplessArena {} @@ -378,7 +383,7 @@ impl DroplessArena { // Also ensure that this chunk can fit `additional`. new_cap = cmp::max(additional, new_cap); - let mut chunk = TypedArenaChunk::::new(new_cap); + let mut chunk = ArenaChunk::new(new_cap); self.start.set(chunk.start()); self.end.set(chunk.end()); chunks.push(chunk); @@ -520,10 +525,19 @@ impl DroplessArena { } } -// Declare an `Arena` containing one dropless arena and many typed arenas (the -// types of the typed arenas are specified by the arguments). The dropless -// arena will be used for any types that impl `Copy`, and also for any of the -// specified types that satisfy `!mem::needs_drop`. +/// Declare an `Arena` containing one dropless arena and many typed arenas (the +/// types of the typed arenas are specified by the arguments). +/// +/// There are three cases of interest. +/// - Types that are `Copy`: these need not be specified in the arguments. They +/// will use the `DroplessArena`. +/// - Types that are `!Copy` and `!Drop`: these must be specified in the +/// arguments. An empty `TypedArena` will be created for each one, but the +/// `DroplessArena` will always be used and the `TypedArena` will stay empty. +/// This is odd but harmless, because an empty arena allocates no memory. +/// - Types that are `!Copy` and `Drop`: these must be specified in the +/// arguments. The `TypedArena` will be used for them. +/// #[rustc_macro_transparency = "semitransparent"] pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { #[derive(Default)] @@ -532,7 +546,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { $($name: $crate::TypedArena<$ty>,)* } - pub trait ArenaAllocatable<'tcx, T = Self>: Sized { + pub trait ArenaAllocatable<'tcx, C = rustc_arena::IsNotCopy>: Sized { fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self; fn allocate_from_iter<'a>( arena: &'a Arena<'tcx>, @@ -541,7 +555,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { } // Any type that impls `Copy` can be arena-allocated in the `DroplessArena`. - impl<'tcx, T: Copy> ArenaAllocatable<'tcx, ()> for T { + impl<'tcx, T: Copy> ArenaAllocatable<'tcx, rustc_arena::IsCopy> for T { #[inline] fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self { arena.dropless.alloc(self) @@ -555,7 +569,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { } } $( - impl<'tcx> ArenaAllocatable<'tcx, $ty> for $ty { + impl<'tcx> ArenaAllocatable<'tcx, rustc_arena::IsNotCopy> for $ty { #[inline] fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self { if !::std::mem::needs_drop::() { @@ -581,7 +595,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { impl<'tcx> Arena<'tcx> { #[inline] - pub fn alloc, U>(&self, value: T) -> &mut T { + pub fn alloc, C>(&self, value: T) -> &mut T { value.allocate_on(self) } @@ -594,7 +608,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { self.dropless.alloc_slice(value) } - pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, U>, U>( + pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, C>, C>( &'a self, iter: impl ::std::iter::IntoIterator, ) -> &'a mut [T] { @@ -603,5 +617,10 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { } } +// Marker types that let us give different behaviour for arenas allocating +// `Copy` types vs `!Copy` types. +pub struct IsCopy; +pub struct IsNotCopy; + #[cfg(test)] mod tests; diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a2d32cdc00..e9135b7163 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -39,6 +39,7 @@ use rustc_span::{Span, DUMMY_SP}; use std::cmp::Ordering; use std::convert::TryFrom; use std::fmt; +use std::mem; #[cfg(test)] mod tests; @@ -53,7 +54,7 @@ mod tests; /// ``` /// /// `'outer` is a label. -#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic, Eq, PartialEq)] pub struct Label { pub ident: Ident, } @@ -224,7 +225,7 @@ pub enum AngleBracketedArg { /// Argument for a generic parameter. Arg(GenericArg), /// Constraint for an associated item. - Constraint(AssocTyConstraint), + Constraint(AssocConstraint), } impl AngleBracketedArg { @@ -1266,7 +1267,7 @@ impl Expr { ExprKind::Break(..) => ExprPrecedence::Break, ExprKind::Continue(..) => ExprPrecedence::Continue, ExprKind::Ret(..) => ExprPrecedence::Ret, - ExprKind::InlineAsm(..) | ExprKind::LlvmInlineAsm(..) => ExprPrecedence::InlineAsm, + ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm, ExprKind::MacCall(..) => ExprPrecedence::Mac, ExprKind::Struct(..) => ExprPrecedence::Struct, ExprKind::Repeat(..) => ExprPrecedence::Repeat, @@ -1276,6 +1277,19 @@ impl Expr { ExprKind::Err => ExprPrecedence::Err, } } + + pub fn take(&mut self) -> Self { + mem::replace( + self, + Expr { + id: DUMMY_NODE_ID, + kind: ExprKind::Err, + span: DUMMY_SP, + attrs: ThinVec::new(), + tokens: None, + }, + ) + } } /// Limit types of a range (inclusive or exclusive) @@ -1423,8 +1437,6 @@ pub enum ExprKind { /// Output of the `asm!()` macro. InlineAsm(P), - /// Output of the `llvm_asm!()` macro. - LlvmInlineAsm(P), /// A macro invocation; pre-expansion. MacCall(MacCall), @@ -1845,19 +1857,38 @@ impl UintTy { /// A constraint on an associated type (e.g., `A = Bar` in `Foo` or /// `A: TraitA + TraitB` in `Foo`). #[derive(Clone, Encodable, Decodable, Debug)] -pub struct AssocTyConstraint { +pub struct AssocConstraint { pub id: NodeId, pub ident: Ident, pub gen_args: Option, - pub kind: AssocTyConstraintKind, + pub kind: AssocConstraintKind, pub span: Span, } -/// The kinds of an `AssocTyConstraint`. +/// The kinds of an `AssocConstraint`. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum Term { + Ty(P), + Const(AnonConst), +} + +impl From> for Term { + fn from(v: P) -> Self { + Term::Ty(v) + } +} + +impl From for Term { + fn from(v: AnonConst) -> Self { + Term::Const(v) + } +} + +/// The kinds of an `AssocConstraint`. #[derive(Clone, Encodable, Decodable, Debug)] -pub enum AssocTyConstraintKind { - /// E.g., `A = Bar` in `Foo`. - Equality { ty: P }, +pub enum AssocConstraintKind { + /// E.g., `A = Bar`, `A = 3` in `Foo` where A is an associated type. + Equality { term: Term }, /// E.g. `A: TraitA + TraitB` in `Foo`. Bound { bounds: GenericBounds }, } @@ -1989,7 +2020,7 @@ bitflags::bitflags! { } } -#[derive(Clone, PartialEq, PartialOrd, Encodable, Decodable, Debug, Hash, HashStable_Generic)] +#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Hash, HashStable_Generic)] pub enum InlineAsmTemplatePiece { String(String), Placeholder { operand_idx: usize, modifier: Option, span: Span }, @@ -2076,41 +2107,6 @@ pub struct InlineAsm { pub line_spans: Vec, } -/// Inline assembly dialect. -/// -/// E.g., `"intel"` as in `llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. -#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Hash, HashStable_Generic)] -pub enum LlvmAsmDialect { - Att, - Intel, -} - -/// LLVM-style inline assembly. -/// -/// E.g., `"={eax}"(result)` as in `llvm_asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`. -#[derive(Clone, Encodable, Decodable, Debug)] -pub struct LlvmInlineAsmOutput { - pub constraint: Symbol, - pub expr: P, - pub is_rw: bool, - pub is_indirect: bool, -} - -/// LLVM-style inline assembly. -/// -/// E.g., `llvm_asm!("NOP");`. -#[derive(Clone, Encodable, Decodable, Debug)] -pub struct LlvmInlineAsm { - pub asm: Symbol, - pub asm_str_style: StrStyle, - pub outputs: Vec, - pub inputs: Vec<(Symbol, P)>, - pub clobbers: Vec, - pub volatile: bool, - pub alignstack: bool, - pub dialect: LlvmAsmDialect, -} - /// A parameter in a function header. /// /// E.g., `bar: usize` as in `fn foo(bar: usize)`. @@ -2229,7 +2225,7 @@ pub enum IsAuto { No, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, Debug)] #[derive(HashStable_Generic)] pub enum Unsafe { Yes(Span), @@ -2422,8 +2418,9 @@ impl rustc_serialize::Encodable for AttrId { } impl rustc_serialize::Decodable for AttrId { - fn decode(d: &mut D) -> Result { - d.read_nil().map(|_| crate::attr::mk_attr_id()) + fn decode(d: &mut D) -> AttrId { + d.read_unit(); + crate::attr::mk_attr_id() } } diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs index b9c397974a..9a24158ba3 100644 --- a/compiler/rustc_ast/src/ast_like.rs +++ b/compiler/rustc_ast/src/ast_like.rs @@ -6,12 +6,13 @@ use super::{AssocItem, Expr, ForeignItem, Item, Local, MacCallStmt}; use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; use super::{AttrVec, Attribute, Stmt, StmtKind}; -use std::fmt::Debug; +use std::fmt; +use std::marker::PhantomData; /// An `AstLike` represents an AST node (or some wrapper around /// and AST node) which stores some combination of attributes /// and tokens. -pub trait AstLike: Sized + Debug { +pub trait AstLike: Sized + fmt::Debug { /// This is `true` if this `AstLike` might support 'custom' (proc-macro) inner /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not /// considered 'custom' attributes @@ -285,3 +286,37 @@ derive_has_attrs_no_tokens! { derive_has_tokens_no_attrs! { Ty, Block, AttrItem, Pat, Path, Visibility } + +/// A newtype around an `AstLike` node that implements `AstLike` itself. +pub struct AstLikeWrapper { + pub wrapped: Wrapped, + pub tag: PhantomData, +} + +impl AstLikeWrapper { + pub fn new(wrapped: Wrapped, _tag: Tag) -> AstLikeWrapper { + AstLikeWrapper { wrapped, tag: Default::default() } + } +} + +impl fmt::Debug for AstLikeWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AstLikeWrapper") + .field("wrapped", &self.wrapped) + .field("tag", &self.tag) + .finish() + } +} + +impl AstLike for AstLikeWrapper { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = Wrapped::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { + self.wrapped.attrs() + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + self.wrapped.visit_attrs(f) + } + fn tokens_mut(&mut self) -> Option<&mut Option> { + self.wrapped.tokens_mut() + } +} diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index d66774040f..b94b8c8721 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -230,7 +230,7 @@ impl AttrItem { } pub fn meta_kind(&self) -> Option { - Some(MetaItemKind::from_mac_args(&self.args)?) + MetaItemKind::from_mac_args(&self.args) } } @@ -242,6 +242,17 @@ impl Attribute { } } + pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + match self.kind { + AttrKind::DocComment(kind, data) => Some((data, kind)), + AttrKind::Normal(ref item, _) if item.path == sym::doc => item + .meta_kind() + .and_then(|kind| kind.value_str()) + .map(|data| (data, CommentKind::Line)), + _ => None, + } + } + pub fn doc_str(&self) -> Option { match self.kind { AttrKind::DocComment(.., data) => Some(data), diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index ff3b501a0b..84fe9ad267 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -41,7 +41,7 @@ pub mod tokenstream; pub mod visit; pub use self::ast::*; -pub use self::ast_like::AstLike; +pub use self::ast_like::{AstLike, AstLikeWrapper}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 9ef78aaf66..a81a227629 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -165,8 +165,8 @@ pub trait MutVisitor: Sized { noop_visit_lifetime(l, self); } - fn visit_ty_constraint(&mut self, t: &mut AssocTyConstraint) { - noop_visit_ty_constraint(t, self); + fn visit_constraint(&mut self, t: &mut AssocConstraint) { + noop_visit_constraint(t, self); } fn visit_foreign_mod(&mut self, nm: &mut ForeignMod) { @@ -430,8 +430,8 @@ pub fn noop_flat_map_arm(mut arm: Arm, vis: &mut T) -> SmallVec<[ smallvec![arm] } -pub fn noop_visit_ty_constraint( - AssocTyConstraint { id, ident, gen_args, kind, span }: &mut AssocTyConstraint, +pub fn noop_visit_constraint( + AssocConstraint { id, ident, gen_args, kind, span }: &mut AssocConstraint, vis: &mut T, ) { vis.visit_id(id); @@ -440,12 +440,11 @@ pub fn noop_visit_ty_constraint( vis.visit_generic_args(gen_args); } match kind { - AssocTyConstraintKind::Equality { ref mut ty } => { - vis.visit_ty(ty); - } - AssocTyConstraintKind::Bound { ref mut bounds } => { - visit_bounds(bounds, vis); - } + AssocConstraintKind::Equality { ref mut term } => match term { + Term::Ty(ty) => vis.visit_ty(ty), + Term::Const(c) => vis.visit_anon_const(c), + }, + AssocConstraintKind::Bound { ref mut bounds } => visit_bounds(bounds, vis), } vis.visit_span(span); } @@ -555,7 +554,7 @@ pub fn noop_visit_angle_bracketed_parameter_data( let AngleBracketedArgs { args, span } = data; visit_vec(args, |arg| match arg { AngleBracketedArg::Arg(arg) => vis.visit_generic_arg(arg), - AngleBracketedArg::Constraint(constraint) => vis.visit_ty_constraint(constraint), + AngleBracketedArg::Constraint(constraint) => vis.visit_constraint(constraint), }); vis.visit_span(span); } @@ -1350,23 +1349,6 @@ pub fn noop_visit_expr( visit_opt(expr, |expr| vis.visit_expr(expr)); } ExprKind::InlineAsm(asm) => noop_visit_inline_asm(asm, vis), - ExprKind::LlvmInlineAsm(asm) => { - let LlvmInlineAsm { - asm: _, - asm_str_style: _, - outputs, - inputs, - clobbers: _, - volatile: _, - alignstack: _, - dialect: _, - } = asm.deref_mut(); - for out in outputs { - let LlvmInlineAsmOutput { constraint: _, expr, is_rw: _, is_indirect: _ } = out; - vis.visit_expr(expr); - } - visit_vec(inputs, |(_c, expr)| vis.visit_expr(expr)); - } ExprKind::MacCall(mac) => vis.visit_mac_call(mac), ExprKind::Struct(se) => { let StructExpr { qself, path, fields, rest } = se.deref_mut(); diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs index 9fe87a0a63..70dbda8222 100644 --- a/compiler/rustc_ast/src/ptr.rs +++ b/compiler/rustc_ast/src/ptr.rs @@ -115,8 +115,8 @@ impl fmt::Pointer for P { } impl> Decodable for P { - fn decode(d: &mut D) -> Result, D::Error> { - Decodable::decode(d).map(P) + fn decode(d: &mut D) -> P { + P(Decodable::decode(d)) } } @@ -204,8 +204,8 @@ impl> Encodable for P<[T]> { } impl> Decodable for P<[T]> { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(P::from_vec(Decodable::decode(d)?)) + fn decode(d: &mut D) -> P<[T]> { + P::from_vec(Decodable::decode(d)) } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 51cabb50cd..2174378a56 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -163,7 +163,7 @@ impl Encodable for LazyTokenStream { } impl Decodable for LazyTokenStream { - fn decode(_d: &mut D) -> Result { + fn decode(_d: &mut D) -> Self { panic!("Attempted to decode LazyTokenStream"); } } diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index 80a06fa594..f51b0086dc 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -1,3 +1,4 @@ +use crate::token::CommentKind; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, CharPos, FileName, Pos, Symbol}; @@ -25,7 +26,7 @@ pub struct Comment { /// Makes a doc string more presentable to users. /// Used by rustdoc and perhaps other tools, but not by rustc. -pub fn beautify_doc_string(data: Symbol) -> Symbol { +pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> { let mut i = 0; let mut j = lines.len(); @@ -34,25 +35,37 @@ pub fn beautify_doc_string(data: Symbol) -> Symbol { i += 1; } - while i < j && lines[i].trim().is_empty() { - i += 1; - } // like the first, a last line of all stars should be omitted if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') { j -= 1; } - while j > i && lines[j - 1].trim().is_empty() { - j -= 1; - } - if i != 0 || j != lines.len() { Some((i, j)) } else { None } } - fn get_horizontal_trim(lines: &[&str]) -> Option { + fn get_horizontal_trim<'a>(lines: &'a [&str], kind: CommentKind) -> Option { let mut i = usize::MAX; let mut first = true; + // In case we have doc comments like `/**` or `/*!`, we want to remove stars if they are + // present. However, we first need to strip the empty lines so they don't get in the middle + // when we try to compute the "horizontal trim". + let lines = if kind == CommentKind::Block { + // Whatever happens, we skip the first line. + let mut i = if lines[0].trim_start().starts_with('*') { 0 } else { 1 }; + let mut j = lines.len(); + + while i < j && lines[i].trim().is_empty() { + i += 1; + } + while j > i && lines[j - 1].trim().is_empty() { + j -= 1; + } + &lines[i..j] + } else { + lines + }; + for line in lines { for (j, c) in line.chars().enumerate() { if j > i || !"* \t".contains(c) { @@ -72,7 +85,7 @@ pub fn beautify_doc_string(data: Symbol) -> Symbol { return None; } } - Some(i) + if lines.is_empty() { None } else { Some(lines[0][..i].into()) } } let data_s = data.as_str(); @@ -86,11 +99,18 @@ pub fn beautify_doc_string(data: Symbol) -> Symbol { } else { &mut lines }; - if let Some(horizontal) = get_horizontal_trim(&lines) { + if let Some(horizontal) = get_horizontal_trim(&lines, kind) { changes = true; // remove a "[ \t]*\*" block from each line, if possible for line in lines.iter_mut() { - *line = &line[horizontal + 1..]; + if let Some(tmp) = line.strip_prefix(&horizontal) { + *line = tmp; + if kind == CommentKind::Block + && (*line == "*" || line.starts_with("* ") || line.starts_with("**")) + { + *line = &line[1..]; + } + } } } if changes { diff --git a/compiler/rustc_ast/src/util/comments/tests.rs b/compiler/rustc_ast/src/util/comments/tests.rs index 6d137f3774..11d50603a1 100644 --- a/compiler/rustc_ast/src/util/comments/tests.rs +++ b/compiler/rustc_ast/src/util/comments/tests.rs @@ -5,7 +5,7 @@ use rustc_span::create_default_session_globals_then; fn test_block_doc_comment_1() { create_default_session_globals_then(|| { let comment = "\n * Test \n ** Test\n * Test\n"; - let stripped = beautify_doc_string(Symbol::intern(comment)); + let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block); assert_eq!(stripped.as_str(), " Test \n* Test\n Test"); }) } @@ -14,7 +14,7 @@ fn test_block_doc_comment_1() { fn test_block_doc_comment_2() { create_default_session_globals_then(|| { let comment = "\n * Test\n * Test\n"; - let stripped = beautify_doc_string(Symbol::intern(comment)); + let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block); assert_eq!(stripped.as_str(), " Test\n Test"); }) } @@ -23,21 +23,39 @@ fn test_block_doc_comment_2() { fn test_block_doc_comment_3() { create_default_session_globals_then(|| { let comment = "\n let a: *i32;\n *a = 5;\n"; - let stripped = beautify_doc_string(Symbol::intern(comment)); - assert_eq!(stripped.as_str(), " let a: *i32;\n *a = 5;"); + let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block); + assert_eq!(stripped.as_str(), "let a: *i32;\n*a = 5;"); }) } #[test] fn test_line_doc_comment() { create_default_session_globals_then(|| { - let stripped = beautify_doc_string(Symbol::intern(" test")); + let stripped = beautify_doc_string(Symbol::intern(" test"), CommentKind::Line); assert_eq!(stripped.as_str(), " test"); - let stripped = beautify_doc_string(Symbol::intern("! test")); + let stripped = beautify_doc_string(Symbol::intern("! test"), CommentKind::Line); assert_eq!(stripped.as_str(), "! test"); - let stripped = beautify_doc_string(Symbol::intern("test")); + let stripped = beautify_doc_string(Symbol::intern("test"), CommentKind::Line); assert_eq!(stripped.as_str(), "test"); - let stripped = beautify_doc_string(Symbol::intern("!test")); + let stripped = beautify_doc_string(Symbol::intern("!test"), CommentKind::Line); assert_eq!(stripped.as_str(), "!test"); }) } + +#[test] +fn test_doc_blocks() { + create_default_session_globals_then(|| { + let stripped = + beautify_doc_string(Symbol::intern(" # Returns\n *\n "), CommentKind::Block); + assert_eq!(stripped.as_str(), " # Returns\n\n"); + + let stripped = beautify_doc_string( + Symbol::intern("\n * # Returns\n *\n "), + CommentKind::Block, + ); + assert_eq!(stripped.as_str(), " # Returns\n\n"); + + let stripped = beautify_doc_string(Symbol::intern("\n * a\n "), CommentKind::Block); + assert_eq!(stripped.as_str(), " a\n"); + }) +} diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 6840f092da..73e9297549 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -190,8 +190,8 @@ pub trait Visitor<'ast>: Sized { fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) { walk_generic_arg(self, generic_arg) } - fn visit_assoc_ty_constraint(&mut self, constraint: &'ast AssocTyConstraint) { - walk_assoc_ty_constraint(self, constraint) + fn visit_assoc_constraint(&mut self, constraint: &'ast AssocConstraint) { + walk_assoc_constraint(self, constraint) } fn visit_attribute(&mut self, attr: &'ast Attribute) { walk_attribute(self, attr) @@ -464,7 +464,7 @@ where for arg in &data.args { match arg { AngleBracketedArg::Arg(a) => visitor.visit_generic_arg(a), - AngleBracketedArg::Constraint(c) => visitor.visit_assoc_ty_constraint(c), + AngleBracketedArg::Constraint(c) => visitor.visit_assoc_constraint(c), } } } @@ -486,19 +486,17 @@ where } } -pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>( - visitor: &mut V, - constraint: &'a AssocTyConstraint, -) { +pub fn walk_assoc_constraint<'a, V: Visitor<'a>>(visitor: &mut V, constraint: &'a AssocConstraint) { visitor.visit_ident(constraint.ident); if let Some(ref gen_args) = constraint.gen_args { visitor.visit_generic_args(gen_args.span(), gen_args); } match constraint.kind { - AssocTyConstraintKind::Equality { ref ty } => { - visitor.visit_ty(ty); - } - AssocTyConstraintKind::Bound { ref bounds } => { + AssocConstraintKind::Equality { ref term } => match term { + Term::Ty(ty) => visitor.visit_ty(ty), + Term::Const(c) => visitor.visit_anon_const(c), + }, + AssocConstraintKind::Bound { ref bounds } => { walk_list!(visitor, visit_param_bound, bounds); } } @@ -864,14 +862,6 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac), ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression), ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm), - ExprKind::LlvmInlineAsm(ref ia) => { - for &(_, ref input) in &ia.inputs { - visitor.visit_expr(input) - } - for output in &ia.outputs { - visitor.visit_expr(&output.expr) - } - } ExprKind::Yield(ref optional_expression) => { walk_list!(visitor, visit_expr, optional_expression); } diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 9c28f3c7f5..18fcc99ffb 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -6,7 +6,7 @@ use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_session::parse::feature_err; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{sym, Span}; use rustc_target::asm; use std::collections::hash_map::Entry; use std::fmt::Write; @@ -66,7 +66,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { for (abi_name, abi_span) in &asm.clobber_abis { match asm::InlineAsmClobberAbi::parse( asm_arch, - |feature| self.sess.target_features.contains(&Symbol::intern(feature)), + &self.sess.target_features, &self.sess.target, *abi_name, ) { @@ -129,13 +129,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .operands .iter() .map(|(op, op_sp)| { - let lower_reg = |reg| match reg { + let lower_reg = |reg, is_clobber| match reg { InlineAsmRegOrRegClass::Reg(s) => { asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch { asm::InlineAsmReg::parse( asm_arch, - |feature| sess.target_features.contains(&Symbol::intern(feature)), + &sess.target_features, &sess.target, + is_clobber, s, ) .unwrap_or_else(|e| { @@ -162,24 +163,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let op = match *op { InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In { - reg: lower_reg(reg), + reg: lower_reg(reg, false), expr: self.lower_expr_mut(expr), }, InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out { - reg: lower_reg(reg), + reg: lower_reg(reg, expr.is_none()), late, expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)), }, InlineAsmOperand::InOut { reg, late, ref expr } => { hir::InlineAsmOperand::InOut { - reg: lower_reg(reg), + reg: lower_reg(reg, false), late, expr: self.lower_expr_mut(expr), } } InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => { hir::InlineAsmOperand::SplitInOut { - reg: lower_reg(reg), + reg: lower_reg(reg, false), late, in_expr: self.lower_expr_mut(in_expr), out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)), @@ -373,7 +374,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { err.emit(); } Entry::Vacant(v) => { - v.insert(idx); + if r == reg { + v.insert(idx); + } } } }; diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 082c5bb783..3a7e0a7058 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -97,7 +97,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let ty = l .ty .as_ref() - .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding))); + .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable))); let init = l.kind.init().map(|init| self.lower_expr(init)); let hir_id = self.lower_node_id(l.id); let pat = self.lower_pat(&l.pat); @@ -127,7 +127,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let ty = local .ty .as_ref() - .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding))); + .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable))); let span = self.lower_span(local.span); let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None); let init = self.lower_expr(init); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 75f384405b..d48ff10b97 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,3 +1,5 @@ +use crate::{FnDeclKind, ImplTraitPosition}; + use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; use rustc_ast::attr; @@ -11,7 +13,7 @@ use rustc_hir::def::Res; use rustc_hir::definitions::DefPathData; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Ident}; use rustc_span::DUMMY_SP; impl<'hir> LoweringContext<'_, 'hir> { @@ -53,15 +55,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ParamMode::Optional, 0, ParenthesizedGenericArgs::Err, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), )); let args = self.lower_exprs(args); - hir::ExprKind::MethodCall( - hir_seg, - self.lower_span(seg.ident.span), - args, - self.lower_span(span), - ) + hir::ExprKind::MethodCall(hir_seg, args, self.lower_span(span)) } ExprKind::Binary(binop, ref lhs, ref rhs) => { let binop = self.lower_binop(binop); @@ -79,12 +76,14 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::Cast(ref expr, ref ty) => { let expr = self.lower_expr(expr); - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + let ty = + self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)); hir::ExprKind::Cast(expr, ty) } ExprKind::Type(ref expr, ref ty) => { let expr = self.lower_expr(expr); - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + let ty = + self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)); hir::ExprKind::Type(expr, ty) } ExprKind::AddrOf(k, m, ref ohs) => { @@ -208,7 +207,7 @@ impl<'hir> LoweringContext<'_, 'hir> { qself, path, ParamMode::Optional, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); hir::ExprKind::Path(qpath) } @@ -226,7 +225,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::InlineAsm(ref asm) => { hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm)) } - ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), ExprKind::Struct(ref se) => { let rest = match &se.rest { StructRest::Base(e) => Some(self.lower_expr(e)), @@ -245,7 +243,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &se.qself, &se.path, ParamMode::Optional, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), )), self.arena .alloc_from_iter(se.fields.iter().map(|x| self.lower_expr_field(x))), @@ -393,14 +391,20 @@ impl<'hir> LoweringContext<'_, 'hir> { // If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond` // in a temporary block. fn manage_let_cond(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> { - match cond.kind { - hir::ExprKind::Let(..) => cond, - _ => { - let span_block = - self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None); - self.expr_drop_temps(span_block, cond, AttrVec::new()) + fn has_let_expr<'hir>(expr: &'hir hir::Expr<'hir>) -> bool { + match expr.kind { + hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), + hir::ExprKind::Let(..) => true, + _ => false, } } + if has_let_expr(cond) { + cond + } else { + let reason = DesugaringKind::CondTemporary; + let span_block = self.mark_span_with_reason(reason, cond.span, None); + self.expr_drop_temps(span_block, cond, AttrVec::new()) + } } // We desugar: `'label: while $cond $body` into: @@ -538,7 +542,9 @@ impl<'hir> LoweringContext<'_, 'hir> { body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::ExprKind<'hir> { let output = match ret_ty { - Some(ty) => hir::FnRetTy::Return(self.lower_ty(&ty, ImplTraitContext::disallowed())), + Some(ty) => hir::FnRetTy::Return( + self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock)), + ), None => hir::FnRetTy::DefaultReturn(self.lower_span(span)), }; @@ -625,18 +631,18 @@ impl<'hir> LoweringContext<'_, 'hir> { /// } /// } /// ``` - fn lower_expr_await(&mut self, await_span: Span, expr: &Expr) -> hir::ExprKind<'hir> { - let dot_await_span = expr.span.shrink_to_hi().to(await_span); + fn lower_expr_await(&mut self, dot_await_span: Span, expr: &Expr) -> hir::ExprKind<'hir> { + let full_span = expr.span.to(dot_await_span); match self.generator_kind { Some(hir::GeneratorKind::Async(_)) => {} Some(hir::GeneratorKind::Gen) | None => { let mut err = struct_span_err!( self.sess, - await_span, + dot_await_span, E0728, "`await` is only allowed inside `async` functions and blocks" ); - err.span_label(await_span, "only allowed inside `async` functions and blocks"); + err.span_label(dot_await_span, "only allowed inside `async` functions and blocks"); if let Some(item_sp) = self.current_item { err.span_label(item_sp, "this is not `async`"); } @@ -646,7 +652,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let span = self.mark_span_with_reason(DesugaringKind::Await, dot_await_span, None); let gen_future_span = self.mark_span_with_reason( DesugaringKind::Await, - await_span, + full_span, self.allow_gen_future.clone(), ); let expr = self.lower_expr_mut(expr); @@ -699,9 +705,9 @@ impl<'hir> LoweringContext<'_, 'hir> { let loop_hir_id = self.lower_node_id(loop_node_id); let ready_arm = { let x_ident = Ident::with_dummy_span(sym::result); - let (x_pat, x_pat_hid) = self.pat_ident(span, x_ident); - let x_expr = self.expr_ident(span, x_ident, x_pat_hid); - let ready_field = self.single_pat_field(span, x_pat); + let (x_pat, x_pat_hid) = self.pat_ident(gen_future_span, x_ident); + let x_expr = self.expr_ident(gen_future_span, x_ident, x_pat_hid); + let ready_field = self.single_pat_field(gen_future_span, x_pat); let ready_pat = self.pat_lang_item_variant( span, hir::LangItem::PollReady, @@ -711,7 +717,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let break_x = self.with_loop_scope(loop_node_id, move |this| { let expr_break = hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr)); - this.arena.alloc(this.expr(span, expr_break, ThinVec::new())) + this.arena.alloc(this.expr(gen_future_span, expr_break, ThinVec::new())) }); self.arm(ready_pat, break_x) }; @@ -783,7 +789,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // `match ::std::future::IntoFuture::into_future() { ... }` let into_future_span = self.mark_span_with_reason( DesugaringKind::Await, - await_span, + dot_await_span, self.allow_into_future.clone(), ); let into_future_expr = self.expr_call_lang_item_fn( @@ -827,7 +833,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }); // Lower outside new scope to preserve `is_in_loop_condition`. - let fn_decl = self.lower_fn_decl(decl, None, false, None); + let fn_decl = self.lower_fn_decl(decl, None, FnDeclKind::Closure, None); hir::ExprKind::Closure( capture_clause, @@ -919,7 +925,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // 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); + let fn_decl = self.lower_fn_decl(&outer_decl, None, FnDeclKind::Closure, None); hir::ExprKind::Closure( capture_clause, @@ -1064,7 +1070,7 @@ impl<'hir> LoweringContext<'_, 'hir> { qself, path, ParamMode::Optional, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); // Destructure like a tuple struct. let tuple_struct_pat = @@ -1089,7 +1095,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &se.qself, &se.path, ParamMode::Optional, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); let fields_omitted = match &se.rest { StructRest::Base(e) => { @@ -1204,11 +1210,13 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let fields = self.arena.alloc_from_iter( - e1.iter().map(|e| ("start", e)).chain(e2.iter().map(|e| ("end", e))).map(|(s, e)| { - let expr = self.lower_expr(&e); - let ident = Ident::new(Symbol::intern(s), self.lower_span(e.span)); - self.expr_field(ident, expr, e.span) - }), + e1.iter().map(|e| (sym::start, e)).chain(e2.iter().map(|e| (sym::end, e))).map( + |(s, e)| { + let expr = self.lower_expr(&e); + let ident = Ident::new(s, self.lower_span(e.span)); + self.expr_field(ident, expr, e.span) + }, + ), ); hir::ExprKind::Struct( @@ -1284,38 +1292,6 @@ impl<'hir> LoweringContext<'_, 'hir> { result } - fn lower_expr_llvm_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> { - let inner = hir::LlvmInlineAsmInner { - inputs: asm.inputs.iter().map(|&(c, _)| c).collect(), - outputs: asm - .outputs - .iter() - .map(|out| hir::LlvmInlineAsmOutput { - constraint: out.constraint, - is_rw: out.is_rw, - is_indirect: out.is_indirect, - span: self.lower_span(out.expr.span), - }) - .collect(), - asm: asm.asm, - asm_str_style: asm.asm_str_style, - clobbers: asm.clobbers.clone(), - volatile: asm.volatile, - alignstack: asm.alignstack, - dialect: asm.dialect, - }; - let hir_asm = hir::LlvmInlineAsm { - inner, - inputs_exprs: self.arena.alloc_from_iter( - asm.inputs.iter().map(|&(_, ref input)| self.lower_expr_mut(input)), - ), - outputs_exprs: self - .arena - .alloc_from_iter(asm.outputs.iter().map(|out| self.lower_expr_mut(&out.expr))), - }; - hir::ExprKind::LlvmInlineAsm(self.arena.alloc(hir_asm)) - } - fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> { hir::ExprField { hir_id: self.next_id(), diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 8a9dad2cdd..62935a2b1f 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -3,7 +3,7 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::definitions; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::*; use rustc_index::vec::{Idx, IndexVec}; use rustc_session::Session; @@ -101,16 +101,10 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { } impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { - type Map = !; - /// Because we want to track parent items and so forth, enable /// deep walking so that we walk nested items in the context of /// their outer items. - fn nested_visit_map(&mut self) -> NestedVisitorMap { - panic!("`visit_nested_xxx` must be manually implemented in this visitor"); - } - fn visit_nested_item(&mut self, item: ItemId) { debug!("visit_nested_item: {:?}", item); self.insert_nested(item.def_id); @@ -335,7 +329,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_impl_item_ref(&mut self, ii: &'hir ImplItemRef) { // Do not visit the duplicate information in ImplItemRef. We want to // map the actual nodes, not the duplicate ones in the *Ref. - let ImplItemRef { id, ident: _, kind: _, span: _, defaultness: _ } = *ii; + let ImplItemRef { id, ident: _, kind: _, span: _, defaultness: _, trait_item_def_id: _ } = + *ii; self.visit_nested_impl_item(id); } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 92cae4da89..6489c729cf 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,6 +1,6 @@ use super::{AnonymousLifetimeMode, LoweringContext, ParamMode}; use super::{ImplTraitContext, ImplTraitPosition}; -use crate::Arena; +use crate::{Arena, FnDeclKind}; use rustc_ast::ptr::P; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; @@ -105,12 +105,11 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> T { let old_len = self.in_scope_lifetimes.len(); - let parent_generics = - match self.owners[parent_hir_id].as_ref().unwrap().node().expect_item().kind { - hir::ItemKind::Impl(hir::Impl { ref generics, .. }) - | hir::ItemKind::Trait(_, _, ref generics, ..) => generics.params, - _ => &[], - }; + let parent_generics = match self.owners[parent_hir_id].unwrap().node().expect_item().kind { + hir::ItemKind::Impl(hir::Impl { ref generics, .. }) + | hir::ItemKind::Trait(_, _, ref generics, ..) => generics.params, + _ => &[], + }; let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind { hir::GenericParamKind::Lifetime { .. } => Some(param.name.normalize_to_macros_2_0()), _ => None, @@ -247,7 +246,12 @@ impl<'hir> LoweringContext<'_, 'hir> { AnonymousLifetimeMode::PassThrough, |this, idty| { let ret_id = asyncness.opt_return_id(); - this.lower_fn_decl(&decl, Some((fn_def_id, idty)), true, ret_id) + this.lower_fn_decl( + &decl, + Some((fn_def_id, idty)), + FnDeclKind::Fn, + ret_id, + ) }, ); let sig = hir::FnSig { @@ -288,12 +292,18 @@ impl<'hir> LoweringContext<'_, 'hir> { capturable_lifetimes: &mut FxHashSet::default(), }, ); - let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); + let generics = self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ); hir::ItemKind::TyAlias(ty, generics) } ItemKind::TyAlias(box TyAlias { ref generics, ty: None, .. }) => { let ty = self.arena.alloc(self.ty(span, hir::TyKind::Err)); - let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); + let generics = self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ); hir::ItemKind::TyAlias(ty, generics) } ItemKind::Enum(ref enum_definition, ref generics) => hir::ItemKind::Enum( @@ -302,20 +312,29 @@ impl<'hir> LoweringContext<'_, 'hir> { enum_definition.variants.iter().map(|x| self.lower_variant(x)), ), }, - self.lower_generics(generics, ImplTraitContext::disallowed()), + self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ), ), ItemKind::Struct(ref struct_def, ref generics) => { let struct_def = self.lower_variant_data(hir_id, struct_def); hir::ItemKind::Struct( struct_def, - self.lower_generics(generics, ImplTraitContext::disallowed()), + self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ), ) } ItemKind::Union(ref vdata, ref generics) => { let vdata = self.lower_variant_data(hir_id, vdata); hir::ItemKind::Union( vdata, - self.lower_generics(generics, ImplTraitContext::disallowed()), + self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ), ) } ItemKind::Impl(box Impl { @@ -348,10 +367,14 @@ impl<'hir> LoweringContext<'_, 'hir> { AnonymousLifetimeMode::CreateParameter, |this, _| { let trait_ref = trait_ref.as_ref().map(|trait_ref| { - this.lower_trait_ref(trait_ref, ImplTraitContext::disallowed()) + this.lower_trait_ref( + trait_ref, + ImplTraitContext::Disallowed(ImplTraitPosition::Trait), + ) }); - let lowered_ty = this.lower_ty(ty, ImplTraitContext::disallowed()); + let lowered_ty = this + .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)); (trait_ref, lowered_ty) }, @@ -391,21 +414,33 @@ impl<'hir> LoweringContext<'_, 'hir> { ref bounds, ref items, }) => { - let bounds = self.lower_param_bounds(bounds, ImplTraitContext::disallowed()); + let bounds = self.lower_param_bounds( + bounds, + ImplTraitContext::Disallowed(ImplTraitPosition::Bound), + ); let items = self .arena .alloc_from_iter(items.iter().map(|item| self.lower_trait_item_ref(item))); hir::ItemKind::Trait( is_auto, self.lower_unsafety(unsafety), - self.lower_generics(generics, ImplTraitContext::disallowed()), + self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ), bounds, items, ) } ItemKind::TraitAlias(ref generics, ref bounds) => hir::ItemKind::TraitAlias( - self.lower_generics(generics, ImplTraitContext::disallowed()), - self.lower_param_bounds(bounds, ImplTraitContext::disallowed()), + self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ), + self.lower_param_bounds( + bounds, + ImplTraitContext::Disallowed(ImplTraitPosition::Bound), + ), ), ItemKind::MacroDef(MacroDef { ref body, macro_rules }) => { let body = P(self.lower_mac_args(body)); @@ -424,7 +459,7 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, body: Option<&Expr>, ) -> (&'hir hir::Ty<'hir>, hir::BodyId) { - let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)); + let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)); (ty, self.lower_const_body(span, body)) } @@ -472,14 +507,18 @@ impl<'hir> LoweringContext<'_, 'hir> { // two imports. for new_node_id in [id1, id2] { let new_id = self.resolver.local_def_id(new_node_id); - let res = if let Some(res) = resolutions.next() { - res - } else { + let Some(res) = resolutions.next() else { // Associate an HirId to both ids even if there is no resolution. let _old = self .node_id_to_hir_id .insert(new_node_id, hir::HirId::make_owner(new_id)); debug_assert!(_old.is_none()); + self.owners.ensure_contains_elem(new_id, || hir::MaybeOwner::Phantom); + let _old = std::mem::replace( + &mut self.owners[new_id], + hir::MaybeOwner::NonOwner(hir::HirId::make_owner(new_id)), + ); + debug_assert!(matches!(_old, hir::MaybeOwner::Phantom)); continue; }; let ident = *ident; @@ -664,7 +703,7 @@ impl<'hir> LoweringContext<'_, 'hir> { |this, _| { ( // Disallow `impl Trait` in foreign items. - this.lower_fn_decl(fdec, None, false, None), + this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None), this.lower_fn_params_to_names(fdec), ) }, @@ -673,7 +712,8 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ForeignItemKind::Fn(fn_dec, fn_args, generics) } ForeignItemKind::Static(ref t, m, _) => { - let ty = self.lower_ty(t, ImplTraitContext::disallowed()); + let ty = + self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Type)); hir::ForeignItemKind::Static(ty, m) } ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type, @@ -741,11 +781,11 @@ impl<'hir> LoweringContext<'_, 'hir> { qself, path, ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124) - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); self.arena.alloc(t) } else { - self.lower_ty(&f.ty, ImplTraitContext::disallowed()) + self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)) }; let hir_id = self.lower_node_id(f.id); self.lower_attrs(hir_id, &f.attrs); @@ -768,14 +808,19 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, kind) = match i.kind { AssocItemKind::Const(_, ref ty, ref default) => { - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)); 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(box Fn { ref sig, ref generics, body: 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); + let (generics, sig) = self.lower_method_sig( + generics, + sig, + trait_item_def_id, + FnDeclKind::Trait, + None, + ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names))) } AssocItemKind::Fn(box Fn { ref sig, ref generics, body: Some(ref body), .. }) => { @@ -786,16 +831,24 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, sig, trait_item_def_id, - false, + FnDeclKind::Trait, asyncness.opt_return_id(), ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id))) } AssocItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. }) => { - let ty = ty.as_ref().map(|x| self.lower_ty(x, ImplTraitContext::disallowed())); - let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); + let ty = ty.as_ref().map(|x| { + self.lower_ty(x, ImplTraitContext::Disallowed(ImplTraitPosition::Type)) + }); + let generics = self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ); let kind = hir::TraitItemKind::Type( - self.lower_param_bounds(bounds, ImplTraitContext::disallowed()), + self.lower_param_bounds( + bounds, + ImplTraitContext::Disallowed(ImplTraitPosition::Bound), + ), ty, ); @@ -847,7 +900,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (generics, kind) = match &i.kind { AssocItemKind::Const(_, ty, expr) => { - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)); ( hir::Generics::empty(), hir::ImplItemKind::Const(ty, self.lower_const_body(i.span, expr.as_deref())), @@ -858,19 +911,21 @@ impl<'hir> LoweringContext<'_, 'hir> { let asyncness = sig.header.asyncness; let body_id = self.lower_maybe_async_body(i.span, &sig.decl, asyncness, body.as_deref()); - let impl_trait_return_allow = !self.is_in_trait_impl; let (generics, sig) = self.lower_method_sig( generics, sig, impl_item_def_id, - impl_trait_return_allow, + if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, asyncness.opt_return_id(), ); (generics, hir::ImplItemKind::Fn(sig, body_id)) } AssocItemKind::TyAlias(box TyAlias { generics, ty, .. }) => { - let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); + let generics = self.lower_generics( + generics, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ); let kind = match ty { None => { let ty = self.arena.alloc(self.ty(i.span, hir::TyKind::Err)); @@ -891,9 +946,6 @@ impl<'hir> LoweringContext<'_, 'hir> { AssocItemKind::MacCall(..) => panic!("`TyMac` should have been expanded by now"), }; - // Since `default impl` is not yet implemented, this is always true in impls. - let has_value = true; - let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); let hir_id = self.lower_node_id(i.id); self.lower_attrs(hir_id, &i.attrs); let item = hir::ImplItem { @@ -901,7 +953,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ident: self.lower_ident(i.ident), generics, vis: self.lower_visibility(&i.vis), - defaultness, kind, span: self.lower_span(i.span), }; @@ -925,6 +976,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } AssocItemKind::MacCall(..) => unimplemented!(), }, + trait_item_def_id: self.resolver.get_partial_res(i.id).map(|r| r.base_res().def_id()), } } @@ -1248,7 +1300,7 @@ impl<'hir> LoweringContext<'_, 'hir> { generics: &Generics, sig: &FnSig, fn_def_id: LocalDefId, - impl_trait_return_allow: bool, + kind: FnDeclKind, is_async: Option, ) -> (hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header); @@ -1256,14 +1308,7 @@ impl<'hir> LoweringContext<'_, 'hir> { generics, fn_def_id, AnonymousLifetimeMode::PassThrough, - |this, idty| { - this.lower_fn_decl( - &sig.decl, - Some((fn_def_id, idty)), - impl_trait_return_allow, - is_async, - ) - }, + |this, idty| this.lower_fn_decl(&sig.decl, Some((fn_def_id, idty)), kind, is_async), ); (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) } @@ -1409,11 +1454,19 @@ impl<'hir> LoweringContext<'_, 'hir> { span, }) => self.with_in_scope_lifetime_defs(&bound_generic_params, |this| { hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - bound_generic_params: this - .lower_generic_params(bound_generic_params, ImplTraitContext::disallowed()), - bounded_ty: this.lower_ty(bounded_ty, ImplTraitContext::disallowed()), + bound_generic_params: this.lower_generic_params( + bound_generic_params, + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + ), + bounded_ty: this.lower_ty( + bounded_ty, + ImplTraitContext::Disallowed(ImplTraitPosition::Type), + ), bounds: this.arena.alloc_from_iter(bounds.iter().map(|bound| { - this.lower_param_bound(bound, ImplTraitContext::disallowed()) + this.lower_param_bound( + bound, + ImplTraitContext::Disallowed(ImplTraitPosition::Bound), + ) })), span: this.lower_span(span), }) @@ -1425,13 +1478,18 @@ impl<'hir> LoweringContext<'_, 'hir> { }) => hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { span: self.lower_span(span), lifetime: self.lower_lifetime(lifetime), - bounds: self.lower_param_bounds(bounds, ImplTraitContext::disallowed()), + bounds: self.lower_param_bounds( + bounds, + ImplTraitContext::Disallowed(ImplTraitPosition::Bound), + ), }), WherePredicate::EqPredicate(WhereEqPredicate { id, ref lhs_ty, ref rhs_ty, span }) => { hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { hir_id: self.lower_node_id(id), - lhs_ty: self.lower_ty(lhs_ty, ImplTraitContext::disallowed()), - rhs_ty: self.lower_ty(rhs_ty, ImplTraitContext::disallowed()), + lhs_ty: self + .lower_ty(lhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)), + rhs_ty: self + .lower_ty(rhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)), span: self.lower_span(span), }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 35eb716949..ae7f22923d 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -32,8 +32,10 @@ #![feature(crate_visibility_modifier)] #![feature(box_patterns)] +#![feature(let_else)] #![feature(never_type)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree}; @@ -98,7 +100,7 @@ struct LoweringContext<'a, 'hir: 'a> { arena: &'hir Arena<'hir>, /// The items being lowered are collected here. - owners: IndexVec>>, + owners: IndexVec>>, /// Bodies inside the owner being lowered. bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>, /// Attributes inside the owner being lowered. @@ -254,19 +256,28 @@ enum ImplTraitContext<'b, 'a> { /// Position in which `impl Trait` is disallowed. #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum ImplTraitPosition { - /// Disallowed in `let` / `const` / `static` bindings. - Binding, - - /// All other positions. - Other, + Path, + Variable, + Type, + Trait, + AsyncBlock, + Bound, + Generic, + ExternFnParam, + ClosureParam, + PointerParam, + FnTraitParam, + TraitParam, + ImplParam, + ExternFnReturn, + ClosureReturn, + PointerReturn, + FnTraitReturn, + TraitReturn, + ImplReturn, } impl<'a> ImplTraitContext<'_, 'a> { - #[inline] - fn disallowed() -> Self { - ImplTraitContext::Disallowed(ImplTraitPosition::Other) - } - fn reborrow<'this>(&'this mut self) -> ImplTraitContext<'this, 'a> { use self::ImplTraitContext::*; match self { @@ -282,6 +293,54 @@ impl<'a> ImplTraitContext<'_, 'a> { } } +impl std::fmt::Display for ImplTraitPosition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name = match self { + ImplTraitPosition::Path => "path", + ImplTraitPosition::Variable => "variable binding", + ImplTraitPosition::Type => "type", + ImplTraitPosition::Trait => "trait", + ImplTraitPosition::AsyncBlock => "async block", + ImplTraitPosition::Bound => "bound", + ImplTraitPosition::Generic => "generic", + ImplTraitPosition::ExternFnParam => "`extern fn` param", + ImplTraitPosition::ClosureParam => "closure param", + ImplTraitPosition::PointerParam => "`fn` pointer param", + ImplTraitPosition::FnTraitParam => "`Fn` trait param", + ImplTraitPosition::TraitParam => "trait method param", + ImplTraitPosition::ImplParam => "`impl` method param", + ImplTraitPosition::ExternFnReturn => "`extern fn` return", + ImplTraitPosition::ClosureReturn => "closure return", + ImplTraitPosition::PointerReturn => "`fn` pointer return", + ImplTraitPosition::FnTraitReturn => "`Fn` trait return", + ImplTraitPosition::TraitReturn => "trait method return", + ImplTraitPosition::ImplReturn => "`impl` method return", + }; + + write!(f, "{}", name) + } +} + +#[derive(Debug)] +enum FnDeclKind { + Fn, + Inherent, + ExternFn, + Closure, + Pointer, + Trait, + Impl, +} + +impl FnDeclKind { + fn impl_trait_return_allowed(&self) -> bool { + match self { + FnDeclKind::Fn | FnDeclKind::Inherent => true, + _ => false, + } + } +} + pub fn lower_crate<'a, 'hir>( sess: &'a Session, krate: &'a Crate, @@ -291,7 +350,8 @@ pub fn lower_crate<'a, 'hir>( ) -> &'hir hir::Crate<'hir> { let _prof_timer = sess.prof.verbose_generic_activity("hir_lowering"); - let owners = IndexVec::from_fn_n(|_| None, resolver.definitions().def_index_count()); + let owners = + IndexVec::from_fn_n(|_| hir::MaybeOwner::Phantom, resolver.definitions().def_index_count()); LoweringContext { sess, resolver, @@ -402,19 +462,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let hir_hash = self.compute_hir_hash(); - let mut def_id_to_hir_id = IndexVec::default(); - - for (node_id, hir_id) in self.node_id_to_hir_id.into_iter_enumerated() { - if let Some(def_id) = self.resolver.opt_local_def_id(node_id) { - if def_id_to_hir_id.len() <= def_id.index() { - def_id_to_hir_id.resize(def_id.index() + 1, None); - } - def_id_to_hir_id[def_id] = hir_id; - } - } - - self.resolver.definitions().init_def_id_to_hir_id_mapping(def_id_to_hir_id); - let krate = hir::Crate { owners: self.owners, hir_hash }; self.arena.alloc(krate) } @@ -427,7 +474,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .owners .iter_enumerated() .filter_map(|(def_id, info)| { - let info = info.as_ref()?; + let info = info.as_owner()?; let def_path_hash = definitions.def_path_hash(def_id); Some((def_path_hash, info)) }) @@ -469,8 +516,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.current_hir_id_owner = current_owner; self.item_local_id_counter = current_local_counter; - let _old = self.owners.insert(def_id, info); - debug_assert!(_old.is_none()); + self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom); + self.owners[def_id] = hir::MaybeOwner::Owner(self.arena.alloc(info)); def_id } @@ -479,6 +526,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let attrs = std::mem::take(&mut self.attrs); let mut bodies = std::mem::take(&mut self.bodies); let local_node_ids = std::mem::take(&mut self.local_node_ids); + + let local_id_to_def_id = local_node_ids + .iter() + .filter_map(|&node_id| { + let hir_id = self.node_id_to_hir_id[node_id]?; + if hir_id.local_id == hir::ItemLocalId::new(0) { + None + } else { + let def_id = self.resolver.opt_local_def_id(node_id)?; + self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom); + if let o @ hir::MaybeOwner::Phantom = &mut self.owners[def_id] { + // Do not override a `MaybeOwner::Owner` that may already here. + *o = hir::MaybeOwner::NonOwner(hir_id); + } + Some((hir_id.local_id, def_id)) + } + }) + .collect(); + let trait_map = local_node_ids .into_iter() .filter_map(|node_id| { @@ -501,7 +567,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (hash_including_bodies, hash_without_bodies) = self.hash_owner(node, &bodies); let (nodes, parenting) = index::index_hir(self.sess, self.resolver.definitions(), node, &bodies); - let nodes = hir::OwnerNodes { hash_including_bodies, hash_without_bodies, nodes, bodies }; + let nodes = hir::OwnerNodes { + hash_including_bodies, + hash_without_bodies, + nodes, + bodies, + local_id_to_def_id, + }; let attrs = { let mut hcx = self.resolver.create_stable_hashing_context(); let mut stable_hasher = StableHasher::new(); @@ -960,7 +1032,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// returns a `hir::TypeBinding` representing `Item`. fn lower_assoc_ty_constraint( &mut self, - constraint: &AssocTyConstraint, + constraint: &AssocConstraint, mut itctx: ImplTraitContext<'_, 'hir>, ) -> hir::TypeBinding<'hir> { debug!("lower_assoc_ty_constraint(constraint={:?}, itctx={:?})", constraint, itctx); @@ -997,10 +1069,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; let kind = match constraint.kind { - AssocTyConstraintKind::Equality { ref ty } => { - hir::TypeBindingKind::Equality { ty: self.lower_ty(ty, itctx) } + AssocConstraintKind::Equality { ref term } => { + let term = match term { + Term::Ty(ref ty) => self.lower_ty(ty, itctx).into(), + Term::Const(ref c) => self.lower_anon_const(c).into(), + }; + hir::TypeBindingKind::Equality { term } } - AssocTyConstraintKind::Bound { ref bounds } => { + AssocConstraintKind::Bound { ref bounds } => { let mut capturable_lifetimes; let mut parent_def_id = self.current_hir_id_owner; // Piggy-back on the `impl Trait` context to figure out the correct behavior. @@ -1078,7 +1154,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { itctx, ); - hir::TypeBindingKind::Equality { ty } + hir::TypeBindingKind::Equality { term: ty.into() } }) } else { // Desugar `AssocTy: Bounds` into a type binding where the @@ -1213,11 +1289,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy { generic_params: this.lower_generic_params( &f.generic_params, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Generic), ), unsafety: this.lower_unsafety(f.unsafety), abi: this.lower_extern(f.ext), - decl: this.lower_fn_decl(&f.decl, None, false, None), + decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None), param_names: this.lower_fn_params_to_names(&f.decl), })) }) @@ -1338,13 +1414,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }), )) } - ImplTraitContext::Disallowed(_) => { + ImplTraitContext::Disallowed(position) => { let mut err = struct_span_err!( self.sess, t.span, E0562, - "`impl Trait` not allowed outside of {}", - "function and method return types", + "`impl Trait` only allowed in function and inherent method return types, not in {}", + position ); err.emit(); hir::TyKind::Err @@ -1509,16 +1585,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, decl: &FnDecl, mut in_band_ty_params: Option<(LocalDefId, &mut Vec>)>, - impl_trait_return_allow: bool, + kind: FnDeclKind, make_ret_async: Option, ) -> &'hir hir::FnDecl<'hir> { debug!( "lower_fn_decl(\ fn_decl: {:?}, \ in_band_ty_params: {:?}, \ - impl_trait_return_allow: {}, \ + kind: {:?}, \ make_ret_async: {:?})", - decl, in_band_ty_params, impl_trait_return_allow, make_ret_async, + decl, in_band_ty_params, kind, make_ret_async, ); let lt_mode = if make_ret_async.is_some() { // In `async fn`, argument-position elided lifetimes @@ -1548,7 +1624,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::Universal(ibty, this.current_hir_id_owner), ) } else { - this.lower_ty_direct(¶m.ty, ImplTraitContext::disallowed()) + this.lower_ty_direct( + ¶m.ty, + ImplTraitContext::Disallowed(match kind { + FnDeclKind::Fn | FnDeclKind::Inherent => { + unreachable!("fn should allow in-band lifetimes") + } + FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam, + FnDeclKind::Closure => ImplTraitPosition::ClosureParam, + FnDeclKind::Pointer => ImplTraitPosition::PointerParam, + FnDeclKind::Trait => ImplTraitPosition::TraitParam, + FnDeclKind::Impl => ImplTraitPosition::ImplParam, + }), + ) } })) }); @@ -1563,13 +1651,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match decl.output { FnRetTy::Ty(ref ty) => { let context = match in_band_ty_params { - Some((def_id, _)) if impl_trait_return_allow => { + Some((def_id, _)) if kind.impl_trait_return_allowed() => { ImplTraitContext::ReturnPositionOpaqueTy { fn_def_id: def_id, origin: hir::OpaqueTyOrigin::FnReturn(def_id), } } - _ => ImplTraitContext::disallowed(), + _ => ImplTraitContext::Disallowed(match kind { + FnDeclKind::Fn | FnDeclKind::Inherent => { + unreachable!("fn should allow in-band lifetimes") + } + FnDeclKind::ExternFn => ImplTraitPosition::ExternFnReturn, + FnDeclKind::Closure => ImplTraitPosition::ClosureReturn, + FnDeclKind::Pointer => ImplTraitPosition::PointerReturn, + FnDeclKind::Trait => ImplTraitPosition::TraitReturn, + FnDeclKind::Impl => ImplTraitPosition::ImplReturn, + }), }; hir::FnRetTy::Return(self.lower_ty(ty, context)) } @@ -1927,7 +2024,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { GenericParamKind::Type { ref default, .. } => { let kind = hir::GenericParamKind::Type { default: default.as_ref().map(|x| { - self.lower_ty(x, ImplTraitContext::Disallowed(ImplTraitPosition::Other)) + self.lower_ty(x, ImplTraitContext::Disallowed(ImplTraitPosition::Type)) }), synthetic: false, }; @@ -1935,9 +2032,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (hir::ParamName::Plain(self.lower_ident(param.ident)), kind) } 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 ty = + self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| { + this.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)) }); let default = default.as_ref().map(|def| self.lower_anon_const(def)); ( @@ -2436,12 +2533,6 @@ fn lifetimes_from_impl_trait_bounds( } impl<'r, 'v> intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r> { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - fn visit_generic_args(&mut self, span: Span, parameters: &'v hir::GenericArgs<'v>) { // Don't collect elided lifetimes used inside of `Fn()` syntax. if parameters.parenthesized { diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index ebae779843..2c331767b8 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -1,3 +1,5 @@ +use crate::ImplTraitPosition; + use super::{ImplTraitContext, LoweringContext, ParamMode}; use rustc_ast::ptr::P; @@ -33,7 +35,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself, path, ParamMode::Optional, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct"); break hir::PatKind::TupleStruct(qpath, pats, ddpos); @@ -49,7 +51,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself, path, ParamMode::Optional, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); break hir::PatKind::Path(qpath); } @@ -59,7 +61,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { qself, path, ParamMode::Optional, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::PatField { diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 46928a1846..b35e3a0716 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,3 +1,5 @@ +use crate::ImplTraitPosition; + use super::{AnonymousLifetimeMode, ImplTraitContext, LoweringContext, ParamMode}; use super::{GenericArgsCtor, ParenthesizedGenericArgs}; @@ -184,7 +186,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_mode, 0, ParenthesizedGenericArgs::Err, - ImplTraitContext::disallowed(), + ImplTraitContext::Disallowed(ImplTraitPosition::Path), ) })), span: self.lower_span(p.span), @@ -392,11 +394,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // we generally don't permit such things (see #51008). self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| { 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 inputs = this.arena.alloc_from_iter(inputs.iter().map(|ty| { + this.lower_ty_direct( + ty, + ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam), + ) + })); let output_ty = match output { - FnRetTy::Ty(ty) => this.lower_ty(&ty, ImplTraitContext::disallowed()), + FnRetTy::Ty(ty) => this + .lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)), FnRetTy::Default(_) => this.arena.alloc(this.ty_tup(*span, &[])), }; let args = smallvec![GenericArg::Type(this.ty_tup(*inputs_span, inputs))]; @@ -420,7 +426,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ty: &'hir hir::Ty<'hir>, ) -> hir::TypeBinding<'hir> { let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME); - let kind = hir::TypeBindingKind::Equality { ty }; + let kind = hir::TypeBindingKind::Equality { term: ty.into() }; let args = arena_vec![self;]; let bindings = arena_vec![self;]; let gen_args = self.arena.alloc(hir::GenericArgs { diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 9312a68bc6..45b7042033 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] -itertools = "0.9" +itertools = "0.10" tracing = "0.1" rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 6237a01f69..eb7c75cac0 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -138,10 +138,10 @@ impl<'a> AstValidator<'a> { self.outer_impl_trait = old; } - fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) { + fn visit_assoc_constraint_from_generic_args(&mut self, constraint: &'a AssocConstraint) { match constraint.kind { - AssocTyConstraintKind::Equality { .. } => {} - AssocTyConstraintKind::Bound { .. } => { + AssocConstraintKind::Equality { .. } => {} + AssocConstraintKind::Bound { .. } => { if self.is_assoc_ty_bound_banned { self.err_handler().span_err( constraint.span, @@ -150,7 +150,7 @@ impl<'a> AstValidator<'a> { } } } - self.visit_assoc_ty_constraint(constraint); + self.visit_assoc_constraint(constraint); } // Mirrors `visit::walk_ty`, but tracks relevant state. @@ -960,15 +960,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { return; } ExprKind::Let(..) if !let_allowed => this.ban_let_expr(expr), - ExprKind::LlvmInlineAsm(..) if !this.session.target.allow_asm => { - struct_span_err!( - this.session, - expr.span, - E0472, - "llvm_asm! is unsupported on this target" - ) - .emit(); - } ExprKind::Match(expr, arms) => { this.visit_expr(expr); for arm in arms { @@ -1286,7 +1277,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { // are allowed to contain nested `impl Trait`. AngleBracketedArg::Constraint(constraint) => { self.with_impl_trait(None, |this| { - this.visit_assoc_ty_constraint_from_generic_args(constraint); + this.visit_assoc_constraint_from_generic_args(constraint); }); } } @@ -1595,12 +1586,12 @@ fn deny_equality_constraints( let len = assoc_path.segments.len() - 1; let gen_args = args.as_ref().map(|p| (**p).clone()); // Build ``. - let arg = AngleBracketedArg::Constraint(AssocTyConstraint { + let arg = AngleBracketedArg::Constraint(AssocConstraint { id: rustc_ast::node_id::DUMMY_NODE_ID, ident: *ident, gen_args, - kind: AssocTyConstraintKind::Equality { - ty: predicate.rhs_ty.clone(), + kind: AssocConstraintKind::Equality { + term: predicate.rhs_ty.clone().into(), }, span: ident.span, }); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 85e35c942b..6c44fb0df2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,6 +1,6 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; -use rustc_ast::{AssocTyConstraint, AssocTyConstraintKind, NodeId}; +use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId}; use rustc_ast::{PatKind, RangeEnd, VariantData}; use rustc_errors::struct_span_err; use rustc_feature::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; @@ -196,6 +196,54 @@ impl<'a> PostExpansionVisitor<'a> { "thiscall-unwind ABI is experimental and subject to change" ); } + "cdecl-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "cdecl-unwind ABI is experimental and subject to change" + ); + } + "fastcall-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "fastcall-unwind ABI is experimental and subject to change" + ); + } + "vectorcall-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "vectorcall-unwind ABI is experimental and subject to change" + ); + } + "aapcs-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "aapcs-unwind ABI is experimental and subject to change" + ); + } + "win64-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "win64-unwind ABI is experimental and subject to change" + ); + } + "sysv64-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "sysv64-unwind ABI is experimental and subject to change" + ); + } "wasm" => { gate_feature_post!( &self, @@ -622,8 +670,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_fn(self, fn_kind, span) } - fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) { - if let AssocTyConstraintKind::Bound { .. } = constraint.kind { + fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) { + if let AssocConstraintKind::Bound { .. } = constraint.kind { gate_feature_post!( &self, associated_type_bounds, @@ -631,7 +679,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { "associated type bounds are unstable" ) } - visit::walk_assoc_ty_constraint(self, constraint) + visit::walk_assoc_constraint(self, constraint) } fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { @@ -707,11 +755,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { "`if let` guards are experimental", "you can write `if matches!(, )` instead of `if let = `" ); - gate_all!( - let_chains, - "`let` expressions in this position are experimental", - "you can write `matches!(, )` instead of `let = `" - ); + gate_all!(let_chains, "`let` expressions in this position are unstable"); gate_all!( async_closure, "async closures are unstable", @@ -724,6 +768,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(half_open_range_patterns, "half-open range patterns are unstable"); gate_all!(inline_const, "inline-const is experimental"); gate_all!(inline_const_pat, "inline-const in pattern position is experimental"); + gate_all!(associated_const_equality, "associated const equality is incomplete"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). @@ -778,7 +823,7 @@ fn maybe_stage_features(sess: &Session, krate: &ast::Crate) { ); let mut all_stable = true; for ident in - attr.meta_item_list().into_iter().flatten().map(|nested| nested.ident()).flatten() + attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) { let name = ident.name; let stable_since = lang_features diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index adc4d117b8..f4863137bd 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -8,6 +8,7 @@ #![feature(box_patterns)] #![feature(let_else)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] pub mod ast_validation; pub mod feature_gate; diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index 3980e6da68..a4a48cc8e8 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -126,9 +126,9 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_generic_args(self, path_span, generic_args) } - fn visit_assoc_ty_constraint(&mut self, constraint: &AssocTyConstraint) { + fn visit_assoc_constraint(&mut self, constraint: &AssocConstraint) { self.count += 1; - walk_assoc_ty_constraint(self, constraint) + walk_assoc_constraint(self, constraint) } fn visit_attribute(&mut self, _attr: &Attribute) { self.count += 1; diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index 29f2be4cf4..5ad8714e9f 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -7,6 +7,5 @@ edition = "2021" doctest = false [dependencies] -tracing = "0.1" rustc_span = { path = "../rustc_span" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index ea298d28e7..ddce86f216 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -132,10 +132,14 @@ //! methods called `Printer::scan_*`, and the 'PRINT' process is the //! method called `Printer::print`. +mod convenience; +mod ring; + +use ring::RingBuffer; use std::borrow::Cow; +use std::cmp; use std::collections::VecDeque; -use std::fmt; -use tracing::debug; +use std::iter; /// How to break. Described in more detail in the module docs. #[derive(Clone, Copy, PartialEq)] @@ -144,19 +148,36 @@ pub enum Breaks { Inconsistent, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] +enum IndentStyle { + /// Vertically aligned under whatever column this block begins at. + /// + /// fn demo(arg1: usize, + /// arg2: usize); + Visual, + /// Indented relative to the indentation level of the previous line. + /// + /// fn demo( + /// arg1: usize, + /// arg2: usize, + /// ); + Block { offset: isize }, +} + +#[derive(Clone, Copy, Default, PartialEq)] pub struct BreakToken { offset: isize, blank_space: isize, + pre_break: Option, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] pub struct BeginToken { - offset: isize, + indent: IndentStyle, breaks: Breaks, } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum Token { // In practice a string token contains either a `&'static str` or a // `String`. `Cow` is overkill for this because we never modify the data, @@ -165,99 +186,27 @@ pub enum Token { Break(BreakToken), Begin(BeginToken), End, - Eof, -} - -impl Token { - crate fn is_eof(&self) -> bool { - matches!(self, Token::Eof) - } - - pub fn is_hardbreak_tok(&self) -> bool { - matches!(self, Token::Break(BreakToken { offset: 0, blank_space: SIZE_INFINITY })) - } -} - -impl fmt::Display for Token { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Token::String(ref s) => write!(f, "STR({},{})", s, s.len()), - Token::Break(_) => f.write_str("BREAK"), - Token::Begin(_) => f.write_str("BEGIN"), - Token::End => f.write_str("END"), - Token::Eof => f.write_str("EOF"), - } - } -} - -fn buf_str(buf: &[BufEntry], left: usize, right: usize, lim: usize) -> String { - let n = buf.len(); - let mut i = left; - let mut l = lim; - let mut s = String::from("["); - while i != right && l != 0 { - l -= 1; - if i != left { - s.push_str(", "); - } - s.push_str(&format!("{}={}", buf[i].size, &buf[i].token)); - i += 1; - i %= n; - } - s.push(']'); - s } #[derive(Copy, Clone)] -enum PrintStackBreak { +enum PrintFrame { Fits, - Broken(Breaks), -} - -#[derive(Copy, Clone)] -struct PrintStackElem { - offset: isize, - pbreak: PrintStackBreak, + Broken { indent: usize, breaks: Breaks }, } const SIZE_INFINITY: isize = 0xffff; -pub fn mk_printer() -> Printer { - let linewidth = 78; - // Yes 55, it makes the ring buffers big enough to never fall behind. - let n: usize = 55 * linewidth; - debug!("mk_printer {}", linewidth); - Printer { - out: String::new(), - buf_max_len: n, - margin: linewidth as isize, - space: linewidth as isize, - left: 0, - right: 0, - // Initialize a single entry; advance_right() will extend it on demand - // up to `buf_max_len` elements. - buf: vec![BufEntry::default()], - left_total: 0, - right_total: 0, - scan_stack: VecDeque::new(), - print_stack: Vec::new(), - pending_indentation: 0, - } -} +/// Target line width. +const MARGIN: isize = 78; +/// Every line is allowed at least this much space, even if highly indented. +const MIN_SPACE: isize = 60; pub struct Printer { out: String, - buf_max_len: usize, - /// Width of lines we're constrained to - margin: isize, /// Number of spaces left on line space: isize, - /// Index of left side of input stream - left: usize, - /// Index of right side of input stream - right: usize, /// Ring-buffer of tokens and calculated sizes - buf: Vec, + buf: RingBuffer, /// Running size of stream "...left" left_total: isize, /// Running size of stream "...right" @@ -270,9 +219,14 @@ pub struct Printer { /// advancing. scan_stack: VecDeque, /// Stack of blocks-in-progress being flushed by print - print_stack: Vec, + print_stack: Vec, + /// Level of indentation of current line + indent: usize, /// Buffered indentation to avoid writing trailing whitespace pending_indentation: isize, + /// The token most recently popped from the left boundary of the + /// ring-buffer for printing + last_printed: Option, } #[derive(Clone)] @@ -281,20 +235,33 @@ struct BufEntry { size: isize, } -impl Default for BufEntry { - fn default() -> Self { - BufEntry { token: Token::Eof, size: 0 } +impl Printer { + pub fn new() -> Self { + Printer { + out: String::new(), + space: MARGIN, + buf: RingBuffer::new(), + left_total: 0, + right_total: 0, + scan_stack: VecDeque::new(), + print_stack: Vec::new(), + indent: 0, + pending_indentation: 0, + last_printed: None, + } } -} -impl Printer { - pub fn last_token(&self) -> Token { - self.buf[self.right].token.clone() + pub fn last_token(&self) -> Option<&Token> { + self.last_token_still_buffered().or_else(|| self.last_printed.as_ref()) + } + + pub fn last_token_still_buffered(&self) -> Option<&Token> { + self.buf.last().map(|last| &last.token) } /// Be very careful with this! - pub fn replace_last_token(&mut self, t: Token) { - self.buf[self.right].token = t; + pub fn replace_last_token_still_buffered(&mut self, token: Token) { + self.buf.last_mut().unwrap().token = token; } fn scan_eof(&mut self) { @@ -304,242 +271,170 @@ impl Printer { } } - fn scan_begin(&mut self, b: BeginToken) { + fn scan_begin(&mut self, token: BeginToken) { if self.scan_stack.is_empty() { self.left_total = 1; self.right_total = 1; - self.left = 0; - self.right = 0; - } else { - self.advance_right(); + self.buf.clear(); } - debug!("pp Begin({})/buffer Vec<{},{}>", b.offset, self.left, self.right); - self.scan_push(BufEntry { token: Token::Begin(b), size: -self.right_total }); + let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total }); + self.scan_stack.push_back(right); } fn scan_end(&mut self) { if self.scan_stack.is_empty() { - debug!("pp End/print Vec<{},{}>", self.left, self.right); self.print_end(); } else { - debug!("pp End/buffer Vec<{},{}>", self.left, self.right); - self.advance_right(); - self.scan_push(BufEntry { token: Token::End, size: -1 }); + let right = self.buf.push(BufEntry { token: Token::End, size: -1 }); + self.scan_stack.push_back(right); } } - fn scan_break(&mut self, b: BreakToken) { + fn scan_break(&mut self, token: BreakToken) { if self.scan_stack.is_empty() { self.left_total = 1; self.right_total = 1; - self.left = 0; - self.right = 0; + self.buf.clear(); } else { - self.advance_right(); + self.check_stack(0); } - debug!("pp Break({})/buffer Vec<{},{}>", b.offset, self.left, self.right); - self.check_stack(0); - self.scan_push(BufEntry { token: Token::Break(b), size: -self.right_total }); - self.right_total += b.blank_space; + let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total }); + self.scan_stack.push_back(right); + self.right_total += token.blank_space; } - fn scan_string(&mut self, s: Cow<'static, str>) { + fn scan_string(&mut self, string: Cow<'static, str>) { if self.scan_stack.is_empty() { - debug!("pp String('{}')/print Vec<{},{}>", s, self.left, self.right); - self.print_string(s); + self.print_string(&string); } else { - debug!("pp String('{}')/buffer Vec<{},{}>", s, self.left, self.right); - self.advance_right(); - let len = s.len() as isize; - self.buf[self.right] = BufEntry { token: Token::String(s), size: len }; + let len = string.len() as isize; + self.buf.push(BufEntry { token: Token::String(string), size: len }); self.right_total += len; self.check_stream(); } } + pub fn offset(&mut self, offset: isize) { + if let Some(BufEntry { token: Token::Break(token), .. }) = &mut self.buf.last_mut() { + token.offset += offset; + } + } + fn check_stream(&mut self) { - debug!( - "check_stream Vec<{}, {}> with left_total={}, right_total={}", - self.left, self.right, self.left_total, self.right_total - ); - if self.right_total - self.left_total > self.space { - debug!( - "scan window is {}, longer than space on line ({})", - self.right_total - self.left_total, - self.space - ); - if Some(&self.left) == self.scan_stack.back() { - debug!("setting {} to infinity and popping", self.left); - let scanned = self.scan_pop_bottom(); - self.buf[scanned].size = SIZE_INFINITY; + while self.right_total - self.left_total > self.space { + if *self.scan_stack.front().unwrap() == self.buf.index_of_first() { + self.scan_stack.pop_front().unwrap(); + self.buf.first_mut().unwrap().size = SIZE_INFINITY; } self.advance_left(); - if self.left != self.right { - self.check_stream(); + if self.buf.is_empty() { + break; } } } - fn scan_push(&mut self, entry: BufEntry) { - debug!("scan_push {}", self.right); - self.buf[self.right] = entry; - self.scan_stack.push_front(self.right); - } - - fn scan_pop(&mut self) -> usize { - self.scan_stack.pop_front().unwrap() - } - - fn scan_top(&self) -> usize { - *self.scan_stack.front().unwrap() - } - - fn scan_pop_bottom(&mut self) -> usize { - self.scan_stack.pop_back().unwrap() - } - - fn advance_right(&mut self) { - self.right += 1; - self.right %= self.buf_max_len; - // Extend the buf if necessary. - if self.right == self.buf.len() { - self.buf.push(BufEntry::default()); - } - assert_ne!(self.right, self.left); - } - fn advance_left(&mut self) { - debug!( - "advance_left Vec<{},{}>, sizeof({})={}", - self.left, self.right, self.left, self.buf[self.left].size - ); - - let mut left_size = self.buf[self.left].size; - - while left_size >= 0 { - let left = self.buf[self.left].token.clone(); - - let len = match left { - Token::Break(b) => b.blank_space, - Token::String(ref s) => { - let len = s.len() as isize; - assert_eq!(len, left_size); - len - } - _ => 0, - }; + while self.buf.first().unwrap().size >= 0 { + let left = self.buf.pop_first().unwrap(); - self.print(left, left_size); + match &left.token { + Token::String(string) => { + self.left_total += string.len() as isize; + self.print_string(string); + } + Token::Break(token) => { + self.left_total += token.blank_space; + self.print_break(*token, left.size); + } + Token::Begin(token) => self.print_begin(*token, left.size), + Token::End => self.print_end(), + } - self.left_total += len; + self.last_printed = Some(left.token); - if self.left == self.right { + if self.buf.is_empty() { break; } - - self.left += 1; - self.left %= self.buf_max_len; - - left_size = self.buf[self.left].size; } } - fn check_stack(&mut self, k: usize) { - if !self.scan_stack.is_empty() { - let x = self.scan_top(); - match self.buf[x].token { + fn check_stack(&mut self, mut depth: usize) { + while let Some(&index) = self.scan_stack.back() { + let mut entry = &mut self.buf[index]; + match entry.token { Token::Begin(_) => { - if k > 0 { - self.scan_pop(); - self.buf[x].size += self.right_total; - self.check_stack(k - 1); + if depth == 0 { + break; } + self.scan_stack.pop_back().unwrap(); + entry.size += self.right_total; + depth -= 1; } Token::End => { // paper says + not =, but that makes no sense. - self.scan_pop(); - self.buf[x].size = 1; - self.check_stack(k + 1); + self.scan_stack.pop_back().unwrap(); + entry.size = 1; + depth += 1; } _ => { - self.scan_pop(); - self.buf[x].size += self.right_total; - if k > 0 { - self.check_stack(k); + self.scan_stack.pop_back().unwrap(); + entry.size += self.right_total; + if depth == 0 { + break; } } } } } - fn print_newline(&mut self, amount: isize) { - debug!("NEWLINE {}", amount); - self.out.push('\n'); - self.pending_indentation = 0; - self.indent(amount); - } - - fn indent(&mut self, amount: isize) { - debug!("INDENT {}", amount); - self.pending_indentation += amount; + fn get_top(&self) -> PrintFrame { + *self + .print_stack + .last() + .unwrap_or(&PrintFrame::Broken { indent: 0, breaks: Breaks::Inconsistent }) } - fn get_top(&self) -> PrintStackElem { - *self.print_stack.last().unwrap_or({ - &PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) } - }) - } - - fn print_begin(&mut self, b: BeginToken, l: isize) { - if l > self.space { - let col = self.margin - self.space + b.offset; - debug!("print Begin -> push broken block at col {}", col); - self.print_stack - .push(PrintStackElem { offset: col, pbreak: PrintStackBreak::Broken(b.breaks) }); + fn print_begin(&mut self, token: BeginToken, size: isize) { + if size > self.space { + self.print_stack.push(PrintFrame::Broken { indent: self.indent, breaks: token.breaks }); + self.indent = match token.indent { + IndentStyle::Block { offset } => { + usize::try_from(self.indent as isize + offset).unwrap() + } + IndentStyle::Visual => (MARGIN - self.space) as usize, + }; } else { - debug!("print Begin -> push fitting block"); - self.print_stack.push(PrintStackElem { offset: 0, pbreak: PrintStackBreak::Fits }); + self.print_stack.push(PrintFrame::Fits); } } fn print_end(&mut self) { - debug!("print End -> pop End"); - self.print_stack.pop().unwrap(); + if let PrintFrame::Broken { indent, .. } = self.print_stack.pop().unwrap() { + self.indent = indent; + } } - fn print_break(&mut self, b: BreakToken, l: isize) { - let top = self.get_top(); - match top.pbreak { - PrintStackBreak::Fits => { - debug!("print Break({}) in fitting block", b.blank_space); - self.space -= b.blank_space; - self.indent(b.blank_space); - } - PrintStackBreak::Broken(Breaks::Consistent) => { - debug!("print Break({}+{}) in consistent block", top.offset, b.offset); - self.print_newline(top.offset + b.offset); - self.space = self.margin - (top.offset + b.offset); - } - PrintStackBreak::Broken(Breaks::Inconsistent) => { - if l > self.space { - debug!("print Break({}+{}) w/ newline in inconsistent", top.offset, b.offset); - self.print_newline(top.offset + b.offset); - self.space = self.margin - (top.offset + b.offset); - } else { - debug!("print Break({}) w/o newline in inconsistent", b.blank_space); - self.indent(b.blank_space); - self.space -= b.blank_space; - } + fn print_break(&mut self, token: BreakToken, size: isize) { + let fits = match self.get_top() { + PrintFrame::Fits => true, + PrintFrame::Broken { breaks: Breaks::Consistent, .. } => false, + PrintFrame::Broken { breaks: Breaks::Inconsistent, .. } => size <= self.space, + }; + if fits { + self.pending_indentation += token.blank_space; + self.space -= token.blank_space; + } else { + if let Some(pre_break) = token.pre_break { + self.out.push(pre_break); } + self.out.push('\n'); + let indent = self.indent as isize + token.offset; + self.pending_indentation = indent; + self.space = cmp::max(MARGIN - indent, MIN_SPACE); } } - fn print_string(&mut self, s: Cow<'static, str>) { - let len = s.len() as isize; - debug!("print String({})", s); - // assert!(len <= space); - self.space -= len; - + fn print_string(&mut self, string: &str) { // Write the pending indent. A more concise way of doing this would be: // // write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?; @@ -547,83 +442,10 @@ impl Printer { // But that is significantly slower. This code is sufficiently hot, and indents can get // sufficiently large, that the difference is significant on some workloads. self.out.reserve(self.pending_indentation as usize); - self.out.extend(std::iter::repeat(' ').take(self.pending_indentation as usize)); + self.out.extend(iter::repeat(' ').take(self.pending_indentation as usize)); self.pending_indentation = 0; - self.out.push_str(&s); - } - - fn print(&mut self, token: Token, l: isize) { - debug!("print {} {} (remaining line space={})", token, l, self.space); - debug!("{}", buf_str(&self.buf, self.left, self.right, 6)); - match token { - Token::Begin(b) => self.print_begin(b, l), - Token::End => self.print_end(), - Token::Break(b) => self.print_break(b, l), - Token::String(s) => { - let len = s.len() as isize; - assert_eq!(len, l); - self.print_string(s); - } - Token::Eof => panic!(), // Eof should never get here. - } - } - - // Convenience functions to talk to the printer. - - /// "raw box" - pub fn rbox(&mut self, indent: usize, b: Breaks) { - self.scan_begin(BeginToken { offset: indent as isize, breaks: b }) - } - - /// Inconsistent breaking box - pub fn ibox(&mut self, indent: usize) { - self.rbox(indent, Breaks::Inconsistent) - } - - /// Consistent breaking box - pub fn cbox(&mut self, indent: usize) { - self.rbox(indent, Breaks::Consistent) - } - - pub fn break_offset(&mut self, n: usize, off: isize) { - self.scan_break(BreakToken { offset: off, blank_space: n as isize }) - } - - pub fn end(&mut self) { - self.scan_end() - } - - pub fn eof(mut self) -> String { - self.scan_eof(); - self.out - } - - pub fn word>>(&mut self, wrd: S) { - let s = wrd.into(); - self.scan_string(s) - } - - fn spaces(&mut self, n: usize) { - self.break_offset(n, 0) - } - - crate fn zerobreak(&mut self) { - self.spaces(0) - } - - pub fn space(&mut self) { - self.spaces(1) - } - - pub fn hardbreak(&mut self) { - self.spaces(SIZE_INFINITY as usize) - } - - pub fn is_beginning_of_line(&self) -> bool { - self.last_token().is_eof() || self.last_token().is_hardbreak_tok() - } - pub fn hardbreak_tok_offset(off: isize) -> Token { - Token::Break(BreakToken { offset: off, blank_space: SIZE_INFINITY }) + self.out.push_str(string); + self.space -= string.len() as isize; } } diff --git a/compiler/rustc_ast_pretty/src/pp/convenience.rs b/compiler/rustc_ast_pretty/src/pp/convenience.rs new file mode 100644 index 0000000000..93310dd45c --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pp/convenience.rs @@ -0,0 +1,94 @@ +use crate::pp::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, Token, SIZE_INFINITY}; +use std::borrow::Cow; + +impl Printer { + /// "raw box" + pub fn rbox(&mut self, indent: isize, breaks: Breaks) { + self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks }) + } + + /// Inconsistent breaking box + pub fn ibox(&mut self, indent: isize) { + self.rbox(indent, Breaks::Inconsistent) + } + + /// Consistent breaking box + pub fn cbox(&mut self, indent: isize) { + self.rbox(indent, Breaks::Consistent) + } + + pub fn visual_align(&mut self) { + self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent }); + } + + pub fn break_offset(&mut self, n: usize, off: isize) { + self.scan_break(BreakToken { + offset: off, + blank_space: n as isize, + ..BreakToken::default() + }); + } + + pub fn end(&mut self) { + self.scan_end() + } + + pub fn eof(mut self) -> String { + self.scan_eof(); + self.out + } + + pub fn word>>(&mut self, wrd: S) { + let string = wrd.into(); + self.scan_string(string) + } + + fn spaces(&mut self, n: usize) { + self.break_offset(n, 0) + } + + pub fn zerobreak(&mut self) { + self.spaces(0) + } + + pub fn space(&mut self) { + self.spaces(1) + } + + pub fn hardbreak(&mut self) { + self.spaces(SIZE_INFINITY as usize) + } + + pub fn is_beginning_of_line(&self) -> bool { + match self.last_token() { + Some(last_token) => last_token.is_hardbreak_tok(), + None => true, + } + } + + pub fn hardbreak_tok_offset(off: isize) -> Token { + Token::Break(BreakToken { + offset: off, + blank_space: SIZE_INFINITY, + ..BreakToken::default() + }) + } + + pub fn trailing_comma(&mut self) { + self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); + } + + pub fn trailing_comma_or_space(&mut self) { + self.scan_break(BreakToken { + blank_space: 1, + pre_break: Some(','), + ..BreakToken::default() + }); + } +} + +impl Token { + pub fn is_hardbreak_tok(&self) -> bool { + *self == Printer::hardbreak_tok_offset(0) + } +} diff --git a/compiler/rustc_ast_pretty/src/pp/ring.rs b/compiler/rustc_ast_pretty/src/pp/ring.rs new file mode 100644 index 0000000000..8187394fe3 --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pp/ring.rs @@ -0,0 +1,77 @@ +use std::collections::VecDeque; +use std::ops::{Index, IndexMut}; + +/// A view onto a finite range of an infinitely long sequence of T. +/// +/// The Ts are indexed 0..infinity. A RingBuffer begins as a view of elements +/// 0..0 (i.e. nothing). The user of the RingBuffer advances its left and right +/// position independently, although only in the positive direction, and only +/// with left <= right at all times. +/// +/// Holding a RingBuffer whose view is elements left..right gives the ability to +/// use Index and IndexMut to access elements i in the infinitely long queue for +/// which left <= i < right. +pub struct RingBuffer { + data: VecDeque, + // Abstract index of data[0] in the infinitely sized queue. + offset: usize, +} + +impl RingBuffer { + pub fn new() -> Self { + RingBuffer { data: VecDeque::new(), offset: 0 } + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn push(&mut self, value: T) -> usize { + let index = self.offset + self.data.len(); + self.data.push_back(value); + index + } + + pub fn clear(&mut self) { + self.data.clear(); + } + + pub fn index_of_first(&self) -> usize { + self.offset + } + + pub fn first(&self) -> Option<&T> { + self.data.front() + } + + pub fn first_mut(&mut self) -> Option<&mut T> { + self.data.front_mut() + } + + pub fn pop_first(&mut self) -> Option { + let first = self.data.pop_front()?; + self.offset += 1; + Some(first) + } + + pub fn last(&self) -> Option<&T> { + self.data.back() + } + + pub fn last_mut(&mut self) -> Option<&mut T> { + self.data.back_mut() + } +} + +impl Index for RingBuffer { + type Output = T; + fn index(&self, index: usize) -> &Self::Output { + &self.data[index.checked_sub(self.offset).unwrap()] + } +} + +impl IndexMut for RingBuffer { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.data[index.checked_sub(self.offset).unwrap()] + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index fa9a20f2e0..b2c62383fb 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1,15 +1,19 @@ +mod delimited; +mod expr; +mod item; + use crate::pp::Breaks::{Consistent, Inconsistent}; use crate::pp::{self, Breaks}; -use rustc_ast::attr; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle}; -use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast::util::parser; use rustc_ast::{self as ast, BlockCheckMode, PatKind, RangeEnd, RangeSyntax}; -use rustc_ast::{GenericArg, MacArgs, ModKind}; +use rustc_ast::{attr, Term}; +use rustc_ast::{GenericArg, MacArgs}; use rustc_ast::{GenericBound, SelfKind, TraitBoundModifier}; use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -20,6 +24,8 @@ use rustc_span::{BytePos, FileName, Span}; use std::borrow::Cow; +pub use self::delimited::IterDelimited; + pub enum MacHeader<'a> { Path(&'a ast::Path), Keyword(&'static str), @@ -89,7 +95,7 @@ pub struct State<'a> { ann: &'a (dyn PpAnn + 'a), } -crate const INDENT_UNIT: usize = 4; +crate const INDENT_UNIT: isize = 4; /// Requires you to pass an input filename and reader so that /// it can scan the input text for comments to copy forward. @@ -103,7 +109,7 @@ pub fn print_crate<'a>( edition: Edition, ) -> String { let mut s = - State { s: pp::mk_printer(), comments: Some(Comments::new(sm, filename, input)), ann }; + State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann }; if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) { // We need to print `#![no_std]` (and its feature gate) so that @@ -210,10 +216,6 @@ pub fn literal_to_string(lit: token::Lit) -> String { out } -fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { - format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) -} - impl std::ops::Deref for State<'_> { type Target = pp::Printer; fn deref(&self) -> &Self::Target { @@ -316,7 +318,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.word(cmnt.lines[0].clone()); self.hardbreak() } else { - self.ibox(0); + self.visual_align(); for line in &cmnt.lines { if !line.is_empty() { self.word(line.clone()); @@ -329,9 +331,9 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere CommentStyle::BlankLine => { // We need to do at least one, possibly two hardbreaks. let twice = match self.last_token() { - pp::Token::String(s) => ";" == s, - pp::Token::Begin(_) => true, - pp::Token::End => true, + Some(pp::Token::String(s)) => ";" == s, + Some(pp::Token::Begin(_)) => true, + Some(pp::Token::End) => true, _ => false, }; if twice { @@ -608,7 +610,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere &mut self, macro_def: &ast::MacroDef, ident: &Ident, - sp: &Span, + sp: Span, print_visibility: impl FnOnce(&mut Self), ) { let (kw, has_bang) = if macro_def.macro_rules { @@ -624,7 +626,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere macro_def.body.delim(), ¯o_def.body.inner_tokens(), true, - *sp, + sp, ); if macro_def.body.need_semicolon() { self.word(";"); @@ -656,7 +658,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere // Outer-box is consistent. self.cbox(INDENT_UNIT); // Head-box is inconsistent. - self.ibox(w.len() + 1); + self.ibox(0); // Keyword that starts the head. if !w.is_empty() { self.word_nbsp(w); @@ -687,11 +689,15 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere fn break_offset_if_not_bol(&mut self, n: usize, off: isize) { if !self.is_beginning_of_line() { self.break_offset(n, off) - } else if off != 0 && self.last_token().is_hardbreak_tok() { - // We do something pretty sketchy here: tuck the nonzero - // offset-adjustment we were going to deposit along with the - // break into the previous hardbreak. - self.replace_last_token(pp::Printer::hardbreak_tok_offset(off)); + } else if off != 0 { + if let Some(last_token) = self.last_token_still_buffered() { + if last_token.is_hardbreak_tok() { + // We do something pretty sketchy here: tuck the nonzero + // offset-adjustment we were going to deposit along with the + // break into the previous hardbreak. + self.replace_last_token_still_buffered(pp::Printer::hardbreak_tok_offset(off)); + } + } } } @@ -910,7 +916,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 } + State { s: pp::Printer::new(), comments: None, ann: &NoAnn } } crate fn commasep_cmnt(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) @@ -938,13 +944,6 @@ impl<'a> State<'a> { self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span) } - crate fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) { - self.print_inner_attributes(attrs); - for item in &nmod.items { - self.print_foreign_item(item); - } - } - pub fn print_opt_lifetime(&mut self, lifetime: &Option) { if let Some(lt) = *lifetime { self.print_lifetime(lt); @@ -952,18 +951,19 @@ impl<'a> State<'a> { } } - pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocTyConstraint) { + pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocConstraint) { self.print_ident(constraint.ident); constraint.gen_args.as_ref().map(|args| self.print_generic_args(args, false)); self.space(); match &constraint.kind { - ast::AssocTyConstraintKind::Equality { ty } => { + ast::AssocConstraintKind::Equality { term } => { self.word_space("="); - self.print_type(ty); - } - ast::AssocTyConstraintKind::Bound { bounds } => { - self.print_type_bounds(":", &*bounds); + match term { + Term::Ty(ty) => self.print_type(ty), + Term::Const(c) => self.print_expr_anon_const(c), + } } + ast::AssocConstraintKind::Bound { bounds } => self.print_type_bounds(":", &*bounds), } } @@ -1056,343 +1056,6 @@ impl<'a> State<'a> { self.end(); } - crate fn print_foreign_item(&mut self, item: &ast::ForeignItem) { - let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; - self.ann.pre(self, AnnNode::SubItem(id)); - self.hardbreak_if_not_bol(); - self.maybe_print_comment(span.lo()); - self.print_outer_attributes(attrs); - match kind { - ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, 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(box ast::TyAlias { - defaultness, - generics, - bounds, - ty, - }) => { - self.print_associated_type( - ident, - generics, - bounds, - ty.as_deref(), - vis, - *defaultness, - ); - } - ast::ForeignItemKind::MacCall(m) => { - self.print_mac(m); - if m.args.need_semicolon() { - self.word(";"); - } - } - } - self.ann.post(self, AnnNode::SubItem(id)) - } - - fn print_item_const( - &mut self, - ident: Ident, - mutbl: Option, - ty: &ast::Ty, - body: Option<&ast::Expr>, - vis: &ast::Visibility, - defaultness: ast::Defaultness, - ) { - self.head(""); - self.print_visibility(vis); - self.print_defaultness(defaultness); - let leading = match mutbl { - None => "const", - Some(ast::Mutability::Not) => "static", - Some(ast::Mutability::Mut) => "static mut", - }; - self.word_space(leading); - self.print_ident(ident); - self.word_space(":"); - self.print_type(ty); - if body.is_some() { - self.space(); - } - self.end(); // end the head-ibox - if let Some(body) = body { - self.word_space("="); - self.print_expr(body); - } - self.word(";"); - self.end(); // end the outer cbox - } - - fn print_associated_type( - &mut self, - ident: Ident, - generics: &ast::Generics, - bounds: &ast::GenericBounds, - ty: Option<&ast::Ty>, - vis: &ast::Visibility, - defaultness: ast::Defaultness, - ) { - self.head(""); - self.print_visibility(vis); - self.print_defaultness(defaultness); - self.word_space("type"); - self.print_ident(ident); - self.print_generic_params(&generics.params); - self.print_type_bounds(":", bounds); - self.print_where_clause(&generics.where_clause); - if let Some(ty) = ty { - self.space(); - self.word_space("="); - self.print_type(ty); - } - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - - /// Pretty-prints an item. - crate fn print_item(&mut self, item: &ast::Item) { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(item.span.lo()); - self.print_outer_attributes(&item.attrs); - self.ann.pre(self, AnnNode::Item(item)); - match item.kind { - ast::ItemKind::ExternCrate(orig_name) => { - self.head(visibility_qualified(&item.vis, "extern crate")); - if let Some(orig_name) = orig_name { - self.print_name(orig_name); - self.space(); - self.word("as"); - self.space(); - } - self.print_ident(item.ident); - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - ast::ItemKind::Use(ref tree) => { - self.head(visibility_qualified(&item.vis, "use")); - self.print_use_tree(tree); - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - ast::ItemKind::Static(ref ty, mutbl, ref body) => { - let def = ast::Defaultness::Final; - self.print_item_const(item.ident, Some(mutbl), ty, body.as_deref(), &item.vis, def); - } - 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(box ast::Fn { defaultness, ref sig, ref generics, ref body }) => { - let body = body.as_deref(); - self.print_fn_full( - sig, - item.ident, - generics, - &item.vis, - defaultness, - body, - &item.attrs, - ); - } - ast::ItemKind::Mod(unsafety, ref mod_kind) => { - self.head(Self::to_string(|s| { - s.print_visibility(&item.vis); - s.print_unsafety(unsafety); - s.word("mod"); - })); - self.print_ident(item.ident); - - match mod_kind { - ModKind::Loaded(items, ..) => { - self.nbsp(); - self.bopen(); - self.print_inner_attributes(&item.attrs); - for item in items { - self.print_item(item); - } - let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); - } - ModKind::Unloaded => { - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - } - } - ast::ItemKind::ForeignMod(ref nmod) => { - self.head(Self::to_string(|s| { - s.print_unsafety(nmod.unsafety); - s.word("extern"); - })); - if let Some(abi) = nmod.abi { - self.print_literal(&abi.as_lit()); - self.nbsp(); - } - self.bopen(); - self.print_foreign_mod(nmod, &item.attrs); - let empty = item.attrs.is_empty() && nmod.items.is_empty(); - self.bclose(item.span, empty); - } - ast::ItemKind::GlobalAsm(ref asm) => { - self.head(visibility_qualified(&item.vis, "global_asm!")); - self.print_inline_asm(asm); - self.end(); - } - ast::ItemKind::TyAlias(box ast::TyAlias { - defaultness, - ref generics, - ref bounds, - ref ty, - }) => { - let ty = ty.as_deref(); - self.print_associated_type( - item.ident, - generics, - bounds, - ty, - &item.vis, - defaultness, - ); - } - ast::ItemKind::Enum(ref enum_definition, ref params) => { - self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis); - } - ast::ItemKind::Struct(ref struct_def, ref generics) => { - self.head(visibility_qualified(&item.vis, "struct")); - self.print_struct(struct_def, generics, item.ident, item.span, true); - } - ast::ItemKind::Union(ref struct_def, ref generics) => { - self.head(visibility_qualified(&item.vis, "union")); - self.print_struct(struct_def, generics, item.ident, item.span, true); - } - ast::ItemKind::Impl(box ast::Impl { - unsafety, - polarity, - defaultness, - constness, - ref generics, - ref of_trait, - ref self_ty, - ref items, - }) => { - self.head(""); - self.print_visibility(&item.vis); - self.print_defaultness(defaultness); - self.print_unsafety(unsafety); - self.word("impl"); - - if generics.params.is_empty() { - self.nbsp(); - } else { - self.print_generic_params(&generics.params); - self.space(); - } - - self.print_constness(constness); - - if let ast::ImplPolarity::Negative(_) = polarity { - self.word("!"); - } - - if let Some(ref t) = *of_trait { - self.print_trait_ref(t); - self.space(); - self.word_space("for"); - } - - self.print_type(self_ty); - self.print_where_clause(&generics.where_clause); - - self.space(); - self.bopen(); - self.print_inner_attributes(&item.attrs); - for impl_item in items { - self.print_assoc_item(impl_item); - } - let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); - } - ast::ItemKind::Trait(box ast::Trait { - is_auto, - unsafety, - ref generics, - ref bounds, - ref items, - .. - }) => { - self.head(""); - self.print_visibility(&item.vis); - self.print_unsafety(unsafety); - self.print_is_auto(is_auto); - self.word_nbsp("trait"); - self.print_ident(item.ident); - self.print_generic_params(&generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - for b in bounds.iter() { - if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b { - self.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b.clone()); - } - } - self.print_type_bounds(":", &real_bounds); - self.print_where_clause(&generics.where_clause); - self.word(" "); - self.bopen(); - self.print_inner_attributes(&item.attrs); - for trait_item in items { - self.print_assoc_item(trait_item); - } - let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); - } - ast::ItemKind::TraitAlias(ref generics, ref bounds) => { - self.head(""); - self.print_visibility(&item.vis); - self.word_nbsp("trait"); - self.print_ident(item.ident); - self.print_generic_params(&generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - // FIXME(durka) this seems to be some quite outdated syntax - for b in bounds.iter() { - if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b { - self.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b.clone()); - } - } - self.nbsp(); - self.print_type_bounds("=", &real_bounds); - self.print_where_clause(&generics.where_clause); - self.word(";"); - } - ast::ItemKind::MacCall(ref mac) => { - self.print_mac(mac); - if mac.args.need_semicolon() { - self.word(";"); - } - } - ast::ItemKind::MacroDef(ref macro_def) => { - self.print_mac_def(macro_def, &item.ident, &item.span, |state| { - state.print_visibility(&item.vis) - }); - } - } - self.ann.post(self, AnnNode::Item(item)) - } - fn print_trait_ref(&mut self, t: &ast::TraitRef) { self.print_path(&t.path, false, 0) } @@ -1410,167 +1073,6 @@ impl<'a> State<'a> { self.print_trait_ref(&t.trait_ref) } - crate fn print_enum_def( - &mut self, - enum_definition: &ast::EnumDef, - generics: &ast::Generics, - ident: Ident, - span: rustc_span::Span, - visibility: &ast::Visibility, - ) { - self.head(visibility_qualified(visibility, "enum")); - self.print_ident(ident); - self.print_generic_params(&generics.params); - self.print_where_clause(&generics.where_clause); - self.space(); - self.print_variants(&enum_definition.variants, span) - } - - crate fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) { - self.bopen(); - for v in variants { - self.space_if_not_bol(); - self.maybe_print_comment(v.span.lo()); - self.print_outer_attributes(&v.attrs); - self.ibox(INDENT_UNIT); - self.print_variant(v); - self.word(","); - self.end(); - self.maybe_print_trailing_comment(v.span, None); - } - let empty = variants.is_empty(); - self.bclose(span, empty) - } - - crate fn print_visibility(&mut self, vis: &ast::Visibility) { - match vis.kind { - ast::VisibilityKind::Public => self.word_nbsp("pub"), - ast::VisibilityKind::Crate(sugar) => match sugar { - ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"), - ast::CrateSugar::JustCrate => self.word_nbsp("crate"), - }, - ast::VisibilityKind::Restricted { ref path, .. } => { - let path = Self::to_string(|s| s.print_path(path, false, 0)); - if path == "self" || path == "super" { - self.word_nbsp(format!("pub({})", path)) - } else { - self.word_nbsp(format!("pub(in {})", path)) - } - } - ast::VisibilityKind::Inherited => {} - } - } - - crate fn print_defaultness(&mut self, defaultness: ast::Defaultness) { - if let ast::Defaultness::Default(_) = defaultness { - self.word_nbsp("default"); - } - } - - crate fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) { - self.nbsp(); - self.bopen(); - - let empty = fields.is_empty(); - if !empty { - self.hardbreak_if_not_bol(); - - for field in fields { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(field.span.lo()); - self.print_outer_attributes(&field.attrs); - self.print_visibility(&field.vis); - self.print_ident(field.ident.unwrap()); - self.word_nbsp(":"); - self.print_type(&field.ty); - self.word(","); - } - } - - self.bclose(span, empty); - } - - crate fn print_struct( - &mut self, - struct_def: &ast::VariantData, - generics: &ast::Generics, - ident: Ident, - span: rustc_span::Span, - print_finalizer: bool, - ) { - self.print_ident(ident); - self.print_generic_params(&generics.params); - match struct_def { - ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => { - if let ast::VariantData::Tuple(..) = struct_def { - self.popen(); - self.commasep(Inconsistent, struct_def.fields(), |s, field| { - s.maybe_print_comment(field.span.lo()); - s.print_outer_attributes(&field.attrs); - s.print_visibility(&field.vis); - s.print_type(&field.ty) - }); - self.pclose(); - } - self.print_where_clause(&generics.where_clause); - if print_finalizer { - self.word(";"); - } - self.end(); - self.end(); // Close the outer-box. - } - ast::VariantData::Struct(ref fields, ..) => { - self.print_where_clause(&generics.where_clause); - self.print_record_struct_body(fields, span); - } - } - } - - crate fn print_variant(&mut self, v: &ast::Variant) { - self.head(""); - self.print_visibility(&v.vis); - let generics = ast::Generics::default(); - self.print_struct(&v.data, &generics, v.ident, v.span, false); - if let Some(ref d) = v.disr_expr { - self.space(); - self.word_space("="); - self.print_expr(&d.value) - } - } - - crate fn print_assoc_item(&mut self, item: &ast::AssocItem) { - let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; - self.ann.pre(self, AnnNode::SubItem(id)); - self.hardbreak_if_not_bol(); - self.maybe_print_comment(span.lo()); - self.print_outer_attributes(attrs); - match kind { - ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, 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(box ast::TyAlias { defaultness, generics, bounds, ty }) => { - self.print_associated_type( - ident, - generics, - bounds, - ty.as_deref(), - vis, - *defaultness, - ); - } - ast::AssocItemKind::MacCall(m) => { - self.print_mac(m); - if m.args.need_semicolon() { - self.word(";"); - } - } - } - self.ann.post(self, AnnNode::SubItem(id)) - } - crate fn print_stmt(&mut self, st: &ast::Stmt) { self.maybe_print_comment(st.span.lo()); match st.kind { @@ -1681,42 +1183,6 @@ impl<'a> State<'a> { self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals()) } - fn print_else(&mut self, els: Option<&ast::Expr>) { - if let Some(_else) = els { - match _else.kind { - // Another `else if` block. - ast::ExprKind::If(ref i, ref then, ref e) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); - self.word(" else if "); - self.print_expr_as_cond(i); - self.space(); - self.print_block(then); - self.print_else(e.as_deref()) - } - // Final `else` block. - ast::ExprKind::Block(ref b, _) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); - self.word(" else "); - self.print_block(b) - } - // Constraints would be great here! - _ => { - panic!("print_if saw if with weird alternative"); - } - } - } - } - - crate fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) { - self.head("if"); - self.print_expr_as_cond(test); - self.space(); - self.print_block(blk); - self.print_else(elseopt) - } - crate fn print_mac(&mut self, m: &ast::MacCall) { self.print_mac_common( Some(MacHeader::Path(&m.path)), @@ -1729,533 +1195,6 @@ impl<'a> State<'a> { ); } - fn print_call_post(&mut self, args: &[P]) { - self.popen(); - self.commasep_exprs(Inconsistent, args); - self.pclose() - } - - crate fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) { - self.print_expr_cond_paren(expr, expr.precedence().order() < prec) - } - - /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in - /// `if cond { ... }`. - crate fn print_expr_as_cond(&mut self, expr: &ast::Expr) { - self.print_expr_cond_paren(expr, Self::cond_needs_par(expr)) - } - - // Does `expr` need parentheses when printed in a condition position? - // - // These cases need parens due to the parse error observed in #26461: `if return {}` - // parses as the erroneous construct `if (return {})`, not `if (return) {}`. - fn cond_needs_par(expr: &ast::Expr) -> bool { - match expr.kind { - ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true, - _ => parser::contains_exterior_struct_lit(expr), - } - } - - /// Prints `expr` or `(expr)` when `needs_par` holds. - fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) { - if needs_par { - self.popen(); - } - self.print_expr(expr); - if needs_par { - self.pclose(); - } - } - - fn print_expr_vec(&mut self, exprs: &[P]) { - self.ibox(INDENT_UNIT); - self.word("["); - self.commasep_exprs(Inconsistent, exprs); - self.word("]"); - self.end(); - } - - fn print_expr_anon_const(&mut self, expr: &ast::AnonConst) { - self.ibox(INDENT_UNIT); - self.word("const"); - self.print_expr(&expr.value); - self.end(); - } - - fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) { - self.ibox(INDENT_UNIT); - self.word("["); - self.print_expr(element); - self.word_space(";"); - self.print_expr(&count.value); - self.word("]"); - self.end(); - } - - fn print_expr_struct( - &mut self, - qself: &Option, - path: &ast::Path, - fields: &[ast::ExprField], - rest: &ast::StructRest, - ) { - if let Some(qself) = qself { - self.print_qpath(path, qself, true); - } else { - self.print_path(path, true, 0); - } - self.word("{"); - self.commasep_cmnt( - Consistent, - fields, - |s, field| { - s.print_outer_attributes(&field.attrs); - s.ibox(INDENT_UNIT); - if !field.is_shorthand { - s.print_ident(field.ident); - s.word_space(":"); - } - s.print_expr(&field.expr); - s.end(); - }, - |f| f.span, - ); - match rest { - ast::StructRest::Base(_) | ast::StructRest::Rest(_) => { - self.ibox(INDENT_UNIT); - if !fields.is_empty() { - self.word(","); - self.space(); - } - self.word(".."); - if let ast::StructRest::Base(ref expr) = *rest { - self.print_expr(expr); - } - self.end(); - } - ast::StructRest::None if !fields.is_empty() => self.word(","), - _ => {} - } - self.word("}"); - } - - fn print_expr_tup(&mut self, exprs: &[P]) { - self.popen(); - self.commasep_exprs(Inconsistent, exprs); - if exprs.len() == 1 { - self.word(","); - } - self.pclose() - } - - fn print_expr_call(&mut self, func: &ast::Expr, args: &[P]) { - let prec = match func.kind { - ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, - _ => parser::PREC_POSTFIX, - }; - - self.print_expr_maybe_paren(func, prec); - self.print_call_post(args) - } - - fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P]) { - let base_args = &args[1..]; - self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX); - self.word("."); - self.print_ident(segment.ident); - if let Some(ref args) = segment.args { - self.print_generic_args(args, true); - } - self.print_call_post(base_args) - } - - fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) { - let assoc_op = AssocOp::from_ast_binop(op.node); - let prec = assoc_op.precedence() as i8; - let fixity = assoc_op.fixity(); - - let (left_prec, right_prec) = match fixity { - Fixity::Left => (prec, prec + 1), - Fixity::Right => (prec + 1, prec), - Fixity::None => (prec + 1, prec + 1), - }; - - let left_prec = match (&lhs.kind, op.node) { - // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is - // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead - // of `(x as i32) < ...`. We need to convince it _not_ to do that. - (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => { - parser::PREC_FORCE_PAREN - } - // We are given `(let _ = a) OP b`. - // - // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens - // as the parser will interpret this as `(let _ = a) OP b`. - // - // - Otherwise, e.g. when we have `(let a = b) < c` in AST, - // parens are required since the parser would interpret `let a = b < c` as - // `let a = (b < c)`. To achieve this, we force parens. - (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { - parser::PREC_FORCE_PAREN - } - _ => left_prec, - }; - - self.print_expr_maybe_paren(lhs, left_prec); - self.space(); - self.word_space(op.node.to_string()); - self.print_expr_maybe_paren(rhs, right_prec) - } - - fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) { - self.word(ast::UnOp::to_string(op)); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) - } - - fn print_expr_addr_of( - &mut self, - kind: ast::BorrowKind, - mutability: ast::Mutability, - expr: &ast::Expr, - ) { - self.word("&"); - match kind { - ast::BorrowKind::Ref => self.print_mutability(mutability, false), - ast::BorrowKind::Raw => { - self.word_nbsp("raw"); - self.print_mutability(mutability, true); - } - } - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) - } - - pub fn print_expr(&mut self, expr: &ast::Expr) { - self.print_expr_outer_attr_style(expr, true) - } - - fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) { - self.maybe_print_comment(expr.span.lo()); - - let attrs = &expr.attrs; - if is_inline { - self.print_outer_attributes_inline(attrs); - } else { - self.print_outer_attributes(attrs); - } - - self.ibox(INDENT_UNIT); - self.ann.pre(self, AnnNode::Expr(expr)); - match expr.kind { - ast::ExprKind::Box(ref expr) => { - self.word_space("box"); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); - } - ast::ExprKind::Array(ref exprs) => { - self.print_expr_vec(exprs); - } - ast::ExprKind::ConstBlock(ref anon_const) => { - self.print_expr_anon_const(anon_const); - } - ast::ExprKind::Repeat(ref element, ref count) => { - self.print_expr_repeat(element, count); - } - ast::ExprKind::Struct(ref se) => { - self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest); - } - ast::ExprKind::Tup(ref exprs) => { - self.print_expr_tup(exprs); - } - ast::ExprKind::Call(ref func, ref args) => { - self.print_expr_call(func, &args); - } - ast::ExprKind::MethodCall(ref segment, ref args, _) => { - self.print_expr_method_call(segment, &args); - } - ast::ExprKind::Binary(op, ref lhs, ref rhs) => { - self.print_expr_binary(op, lhs, rhs); - } - ast::ExprKind::Unary(op, ref expr) => { - self.print_expr_unary(op, expr); - } - ast::ExprKind::AddrOf(k, m, ref expr) => { - self.print_expr_addr_of(k, m, expr); - } - ast::ExprKind::Lit(ref lit) => { - self.print_literal(lit); - } - ast::ExprKind::Cast(ref expr, ref ty) => { - let prec = AssocOp::As.precedence() as i8; - self.print_expr_maybe_paren(expr, prec); - self.space(); - self.word_space("as"); - self.print_type(ty); - } - ast::ExprKind::Type(ref expr, ref ty) => { - let prec = AssocOp::Colon.precedence() as i8; - self.print_expr_maybe_paren(expr, prec); - self.word_space(":"); - self.print_type(ty); - } - ast::ExprKind::Let(ref pat, ref scrutinee, _) => { - self.print_let(pat, scrutinee); - } - ast::ExprKind::If(ref test, ref blk, ref elseopt) => { - self.print_if(test, blk, elseopt.as_deref()) - } - ast::ExprKind::While(ref test, ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - self.head("while"); - self.print_expr_as_cond(test); - self.space(); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - self.head("for"); - self.print_pat(pat); - self.space(); - self.word_space("in"); - self.print_expr_as_cond(iter); - self.space(); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::Loop(ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - self.head("loop"); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::Match(ref expr, ref arms) => { - self.cbox(INDENT_UNIT); - self.ibox(INDENT_UNIT); - self.word_nbsp("match"); - self.print_expr_as_cond(expr); - self.space(); - self.bopen(); - self.print_inner_attributes_no_trailing_hardbreak(attrs); - for arm in arms { - self.print_arm(arm); - } - let empty = attrs.is_empty() && arms.is_empty(); - self.bclose(expr.span, empty); - } - ast::ExprKind::Closure( - capture_clause, - asyncness, - movability, - ref decl, - ref body, - _, - ) => { - self.print_movability(movability); - self.print_asyncness(asyncness); - self.print_capture_clause(capture_clause); - - self.print_fn_params_and_ret(decl, true); - self.space(); - self.print_expr(body); - self.end(); // need to close a box - - // a box will be closed by print_expr, but we didn't want an overall - // wrapper so we closed the corresponding opening. so create an - // empty box to satisfy the close. - self.ibox(0); - } - ast::ExprKind::Block(ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - // containing cbox, will be closed by print-block at } - self.cbox(INDENT_UNIT); - // head-box, will be closed by print-block after { - self.ibox(0); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::Async(capture_clause, _, ref blk) => { - self.word_nbsp("async"); - self.print_capture_clause(capture_clause); - // cbox/ibox in analogy to the `ExprKind::Block` arm above - self.cbox(INDENT_UNIT); - self.ibox(0); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::Await(ref expr) => { - self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); - self.word(".await"); - } - ast::ExprKind::Assign(ref lhs, ref rhs, _) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(lhs, prec + 1); - self.space(); - self.word_space("="); - self.print_expr_maybe_paren(rhs, prec); - } - ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(lhs, prec + 1); - self.space(); - self.word(op.node.to_string()); - self.word_space("="); - self.print_expr_maybe_paren(rhs, prec); - } - ast::ExprKind::Field(ref expr, ident) => { - self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); - self.word("."); - self.print_ident(ident); - } - ast::ExprKind::Index(ref expr, ref index) => { - self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); - self.word("["); - self.print_expr(index); - self.word("]"); - } - ast::ExprKind::Range(ref start, ref end, limits) => { - // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence - // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`. - // Here we use a fake precedence value so that any child with lower precedence than - // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) - let fake_prec = AssocOp::LOr.precedence() as i8; - if let Some(ref e) = *start { - self.print_expr_maybe_paren(e, fake_prec); - } - if limits == ast::RangeLimits::HalfOpen { - self.word(".."); - } else { - self.word("..="); - } - if let Some(ref e) = *end { - self.print_expr_maybe_paren(e, fake_prec); - } - } - ast::ExprKind::Underscore => self.word("_"), - ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0), - ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true), - ast::ExprKind::Break(opt_label, ref opt_expr) => { - self.word("break"); - if let Some(label) = opt_label { - self.space(); - self.print_ident(label.ident); - } - if let Some(ref expr) = *opt_expr { - self.space(); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); - } - } - ast::ExprKind::Continue(opt_label) => { - self.word("continue"); - if let Some(label) = opt_label { - self.space(); - self.print_ident(label.ident); - } - } - ast::ExprKind::Ret(ref result) => { - self.word("return"); - if let Some(ref expr) = *result { - self.word(" "); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); - } - } - ast::ExprKind::InlineAsm(ref a) => { - self.word("asm!"); - self.print_inline_asm(a); - } - ast::ExprKind::LlvmInlineAsm(ref a) => { - self.word("llvm_asm!"); - self.popen(); - self.print_symbol(a.asm, a.asm_str_style); - self.word_space(":"); - - self.commasep(Inconsistent, &a.outputs, |s, out| { - let constraint = out.constraint.as_str(); - let mut ch = constraint.chars(); - match ch.next() { - Some('=') if out.is_rw => { - s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked) - } - _ => s.print_string(&constraint, ast::StrStyle::Cooked), - } - s.popen(); - s.print_expr(&out.expr); - s.pclose(); - }); - self.space(); - self.word_space(":"); - - self.commasep(Inconsistent, &a.inputs, |s, &(co, ref o)| { - s.print_symbol(co, ast::StrStyle::Cooked); - s.popen(); - s.print_expr(o); - s.pclose(); - }); - self.space(); - self.word_space(":"); - - self.commasep(Inconsistent, &a.clobbers, |s, &co| { - s.print_symbol(co, ast::StrStyle::Cooked); - }); - - let mut options = vec![]; - if a.volatile { - options.push("volatile"); - } - if a.alignstack { - options.push("alignstack"); - } - if a.dialect == ast::LlvmAsmDialect::Intel { - options.push("intel"); - } - - if !options.is_empty() { - self.space(); - self.word_space(":"); - self.commasep(Inconsistent, &options, |s, &co| { - s.print_string(co, ast::StrStyle::Cooked); - }); - } - - self.pclose(); - } - ast::ExprKind::MacCall(ref m) => self.print_mac(m), - ast::ExprKind::Paren(ref e) => { - self.popen(); - self.print_expr(e); - self.pclose(); - } - ast::ExprKind::Yield(ref e) => { - self.word("yield"); - - if let Some(ref expr) = *e { - self.space(); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); - } - } - ast::ExprKind::Try(ref e) => { - self.print_expr_maybe_paren(e, parser::PREC_POSTFIX); - self.word("?") - } - ast::ExprKind::TryBlock(ref blk) => { - self.head("try"); - self.print_block_with_attrs(blk, attrs) - } - ast::ExprKind::Err => { - self.popen(); - self.word("/*ERROR*/"); - self.pclose() - } - } - self.ann.post(self, AnnNode::Expr(expr)); - self.end(); - } - fn print_inline_asm(&mut self, asm: &ast::InlineAsm) { enum AsmArg<'a> { Template(String), @@ -2551,48 +1490,6 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::Pat(pat)) } - fn print_arm(&mut self, arm: &ast::Arm) { - // Note, I have no idea why this check is necessary, but here it is. - if arm.attrs.is_empty() { - self.space(); - } - self.cbox(INDENT_UNIT); - self.ibox(0); - self.maybe_print_comment(arm.pat.span.lo()); - self.print_outer_attributes(&arm.attrs); - self.print_pat(&arm.pat); - self.space(); - if let Some(ref e) = arm.guard { - self.word_space("if"); - self.print_expr(e); - self.space(); - } - self.word_space("=>"); - - match arm.body.kind { - ast::ExprKind::Block(ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - - // The block will close the pattern's ibox. - self.print_block_unclosed_indent(blk); - - // If it is a user-provided unsafe block, print a comma after it. - if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules { - self.word(","); - } - } - _ => { - self.end(); // Close the ibox for the pattern. - self.print_expr(&arm.body); - self.word(","); - } - } - self.end(); // Close enclosing cbox. - } - fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) { match explicit_self.node { SelfKind::Value(m) => { @@ -2614,75 +1511,12 @@ impl<'a> State<'a> { } } - fn print_fn_full( - &mut self, - sig: &ast::FnSig, - name: Ident, - generics: &ast::Generics, - vis: &ast::Visibility, - defaultness: ast::Defaultness, - body: Option<&ast::Block>, - attrs: &[ast::Attribute], - ) { - if body.is_some() { - self.head(""); - } - self.print_visibility(vis); - self.print_defaultness(defaultness); - self.print_fn(&sig.decl, sig.header, Some(name), generics); - if let Some(body) = body { - self.nbsp(); - self.print_block_with_attrs(body, attrs); - } else { - self.word(";"); - } - } - - crate fn print_fn( - &mut self, - decl: &ast::FnDecl, - header: ast::FnHeader, - name: Option, - generics: &ast::Generics, - ) { - self.print_fn_header_info(header); - if let Some(name) = name { - self.nbsp(); - self.print_ident(name); - } - self.print_generic_params(&generics.params); - self.print_fn_params_and_ret(decl, false); - self.print_where_clause(&generics.where_clause) - } - - crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) { - let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") }; - self.word(open); - self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure)); - self.word(close); - self.print_fn_ret_ty(&decl.output) - } - - crate fn print_movability(&mut self, movability: ast::Movability) { - match movability { - ast::Movability::Static => self.word_space("static"), - ast::Movability::Movable => {} - } - } - crate fn print_asyncness(&mut self, asyncness: ast::Async) { if asyncness.is_async() { self.word_nbsp("async"); } } - crate fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) { - match capture_clause { - ast::CaptureBy::Value => self.word_space("move"), - ast::CaptureBy::Ref => {} - } - } - pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::GenericBound]) { if !bounds.is_empty() { self.word(prefix); @@ -2777,83 +1611,6 @@ impl<'a> State<'a> { self.word(">"); } - crate fn print_where_clause(&mut self, where_clause: &ast::WhereClause) { - if where_clause.predicates.is_empty() && !where_clause.has_where_token { - return; - } - - self.space(); - self.word_space("where"); - - for (i, predicate) in where_clause.predicates.iter().enumerate() { - if i != 0 { - self.word_space(","); - } - - self.print_where_predicate(predicate); - } - } - - pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) { - match predicate { - ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - bound_generic_params, - bounded_ty, - bounds, - .. - }) => { - self.print_formal_generic_params(bound_generic_params); - self.print_type(bounded_ty); - self.print_type_bounds(":", bounds); - } - ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { - lifetime, - bounds, - .. - }) => { - self.print_lifetime_bounds(*lifetime, bounds); - } - ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { - self.print_type(lhs_ty); - self.space(); - self.word_space("="); - self.print_type(rhs_ty); - } - } - } - - crate fn print_use_tree(&mut self, tree: &ast::UseTree) { - match tree.kind { - ast::UseTreeKind::Simple(rename, ..) => { - self.print_path(&tree.prefix, false, 0); - if let Some(rename) = rename { - self.space(); - self.word_space("as"); - self.print_ident(rename); - } - } - ast::UseTreeKind::Glob => { - if !tree.prefix.segments.is_empty() { - self.print_path(&tree.prefix, false, 0); - self.word("::"); - } - self.word("*"); - } - ast::UseTreeKind::Nested(ref items) => { - if tree.prefix.segments.is_empty() { - self.word("{"); - } else { - self.print_path(&tree.prefix, false, 0); - self.word("::{"); - } - self.commasep(Inconsistent, &items, |this, &(ref tree, _)| { - this.print_use_tree(tree) - }); - self.word("}"); - } - } - } - pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) { match mutbl { ast::Mutability::Mut => self.word_nbsp("mut"), diff --git a/compiler/rustc_ast_pretty/src/pprust/state/delimited.rs b/compiler/rustc_ast_pretty/src/pprust/state/delimited.rs new file mode 100644 index 0000000000..fe0640baaa --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pprust/state/delimited.rs @@ -0,0 +1,41 @@ +use std::iter::Peekable; +use std::mem; +use std::ops::Deref; + +pub struct Delimited { + is_first: bool, + iter: Peekable, +} + +pub trait IterDelimited: Iterator + Sized { + fn delimited(self) -> Delimited { + Delimited { is_first: true, iter: self.peekable() } + } +} + +impl IterDelimited for I {} + +pub struct IteratorItem { + value: T, + pub is_first: bool, + pub is_last: bool, +} + +impl Iterator for Delimited { + type Item = IteratorItem; + + fn next(&mut self) -> Option { + let value = self.iter.next()?; + let is_first = mem::replace(&mut self.is_first, false); + let is_last = self.iter.peek().is_none(); + Some(IteratorItem { value, is_first, is_last }) + } +} + +impl Deref for IteratorItem { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs new file mode 100644 index 0000000000..6435f1b614 --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -0,0 +1,587 @@ +use crate::pp::Breaks::Inconsistent; +use crate::pprust::state::{AnnNode, IterDelimited, PrintState, State, INDENT_UNIT}; + +use rustc_ast::ptr::P; +use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast::{self as ast, BlockCheckMode}; + +impl<'a> State<'a> { + fn print_else(&mut self, els: Option<&ast::Expr>) { + if let Some(_else) = els { + match _else.kind { + // Another `else if` block. + ast::ExprKind::If(ref i, ref then, ref e) => { + self.cbox(INDENT_UNIT - 1); + self.ibox(0); + self.word(" else if "); + self.print_expr_as_cond(i); + self.space(); + self.print_block(then); + self.print_else(e.as_deref()) + } + // Final `else` block. + ast::ExprKind::Block(ref b, _) => { + self.cbox(INDENT_UNIT - 1); + self.ibox(0); + self.word(" else "); + self.print_block(b) + } + // Constraints would be great here! + _ => { + panic!("print_if saw if with weird alternative"); + } + } + } + } + + fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) { + self.head("if"); + self.print_expr_as_cond(test); + self.space(); + self.print_block(blk); + self.print_else(elseopt) + } + + fn print_call_post(&mut self, args: &[P]) { + self.popen(); + self.commasep_exprs(Inconsistent, args); + self.pclose() + } + + fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) { + self.print_expr_cond_paren(expr, expr.precedence().order() < prec) + } + + /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in + /// `if cond { ... }`. + fn print_expr_as_cond(&mut self, expr: &ast::Expr) { + self.print_expr_cond_paren(expr, Self::cond_needs_par(expr)) + } + + // Does `expr` need parentheses when printed in a condition position? + // + // These cases need parens due to the parse error observed in #26461: `if return {}` + // parses as the erroneous construct `if (return {})`, not `if (return) {}`. + pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true, + _ => parser::contains_exterior_struct_lit(expr), + } + } + + /// Prints `expr` or `(expr)` when `needs_par` holds. + pub(super) fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) { + if needs_par { + self.popen(); + } + self.print_expr(expr); + if needs_par { + self.pclose(); + } + } + + fn print_expr_vec(&mut self, exprs: &[P]) { + self.ibox(INDENT_UNIT); + self.word("["); + self.commasep_exprs(Inconsistent, exprs); + self.word("]"); + self.end(); + } + + pub(super) fn print_expr_anon_const(&mut self, expr: &ast::AnonConst) { + self.ibox(INDENT_UNIT); + self.word("const"); + self.print_expr(&expr.value); + self.end(); + } + + fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) { + self.ibox(INDENT_UNIT); + self.word("["); + self.print_expr(element); + self.word_space(";"); + self.print_expr(&count.value); + self.word("]"); + self.end(); + } + + fn print_expr_struct( + &mut self, + qself: &Option, + path: &ast::Path, + fields: &[ast::ExprField], + rest: &ast::StructRest, + ) { + if let Some(qself) = qself { + self.print_qpath(path, qself, true); + } else { + self.print_path(path, true, 0); + } + self.nbsp(); + self.word("{"); + let has_rest = match rest { + ast::StructRest::Base(_) | ast::StructRest::Rest(_) => true, + ast::StructRest::None => false, + }; + if fields.is_empty() && !has_rest { + self.word("}"); + return; + } + self.cbox(0); + for field in fields.iter().delimited() { + self.maybe_print_comment(field.span.hi()); + self.print_outer_attributes(&field.attrs); + if field.is_first { + self.space_if_not_bol(); + } + if !field.is_shorthand { + self.print_ident(field.ident); + self.word_nbsp(":"); + } + self.print_expr(&field.expr); + if !field.is_last || has_rest { + self.word_space(","); + } else { + self.trailing_comma_or_space(); + } + } + if has_rest { + if fields.is_empty() { + self.space(); + } + self.word(".."); + if let ast::StructRest::Base(expr) = rest { + self.print_expr(expr); + } + self.space(); + } + self.offset(-INDENT_UNIT); + self.end(); + self.word("}"); + } + + fn print_expr_tup(&mut self, exprs: &[P]) { + self.popen(); + self.commasep_exprs(Inconsistent, exprs); + if exprs.len() == 1 { + self.word(","); + } + self.pclose() + } + + fn print_expr_call(&mut self, func: &ast::Expr, args: &[P]) { + let prec = match func.kind { + ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, + _ => parser::PREC_POSTFIX, + }; + + self.print_expr_maybe_paren(func, prec); + self.print_call_post(args) + } + + fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P]) { + let base_args = &args[1..]; + self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX); + self.word("."); + self.print_ident(segment.ident); + if let Some(ref args) = segment.args { + self.print_generic_args(args, true); + } + self.print_call_post(base_args) + } + + fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) { + let assoc_op = AssocOp::from_ast_binop(op.node); + let prec = assoc_op.precedence() as i8; + let fixity = assoc_op.fixity(); + + let (left_prec, right_prec) = match fixity { + Fixity::Left => (prec, prec + 1), + Fixity::Right => (prec + 1, prec), + Fixity::None => (prec + 1, prec + 1), + }; + + let left_prec = match (&lhs.kind, op.node) { + // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is + // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead + // of `(x as i32) < ...`. We need to convince it _not_ to do that. + (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => { + parser::PREC_FORCE_PAREN + } + // We are given `(let _ = a) OP b`. + // + // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens + // as the parser will interpret this as `(let _ = a) OP b`. + // + // - Otherwise, e.g. when we have `(let a = b) < c` in AST, + // parens are required since the parser would interpret `let a = b < c` as + // `let a = (b < c)`. To achieve this, we force parens. + (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { + parser::PREC_FORCE_PAREN + } + _ => left_prec, + }; + + self.print_expr_maybe_paren(lhs, left_prec); + self.space(); + self.word_space(op.node.to_string()); + self.print_expr_maybe_paren(rhs, right_prec) + } + + fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) { + self.word(ast::UnOp::to_string(op)); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) + } + + fn print_expr_addr_of( + &mut self, + kind: ast::BorrowKind, + mutability: ast::Mutability, + expr: &ast::Expr, + ) { + self.word("&"); + match kind { + ast::BorrowKind::Ref => self.print_mutability(mutability, false), + ast::BorrowKind::Raw => { + self.word_nbsp("raw"); + self.print_mutability(mutability, true); + } + } + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) + } + + pub fn print_expr(&mut self, expr: &ast::Expr) { + self.print_expr_outer_attr_style(expr, true) + } + + pub(super) fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) { + self.maybe_print_comment(expr.span.lo()); + + let attrs = &expr.attrs; + if is_inline { + self.print_outer_attributes_inline(attrs); + } else { + self.print_outer_attributes(attrs); + } + + self.ibox(INDENT_UNIT); + self.ann.pre(self, AnnNode::Expr(expr)); + match expr.kind { + ast::ExprKind::Box(ref expr) => { + self.word_space("box"); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); + } + ast::ExprKind::Array(ref exprs) => { + self.print_expr_vec(exprs); + } + ast::ExprKind::ConstBlock(ref anon_const) => { + self.print_expr_anon_const(anon_const); + } + ast::ExprKind::Repeat(ref element, ref count) => { + self.print_expr_repeat(element, count); + } + ast::ExprKind::Struct(ref se) => { + self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest); + } + ast::ExprKind::Tup(ref exprs) => { + self.print_expr_tup(exprs); + } + ast::ExprKind::Call(ref func, ref args) => { + self.print_expr_call(func, &args); + } + ast::ExprKind::MethodCall(ref segment, ref args, _) => { + self.print_expr_method_call(segment, &args); + } + ast::ExprKind::Binary(op, ref lhs, ref rhs) => { + self.print_expr_binary(op, lhs, rhs); + } + ast::ExprKind::Unary(op, ref expr) => { + self.print_expr_unary(op, expr); + } + ast::ExprKind::AddrOf(k, m, ref expr) => { + self.print_expr_addr_of(k, m, expr); + } + ast::ExprKind::Lit(ref lit) => { + self.print_literal(lit); + } + ast::ExprKind::Cast(ref expr, ref ty) => { + let prec = AssocOp::As.precedence() as i8; + self.print_expr_maybe_paren(expr, prec); + self.space(); + self.word_space("as"); + self.print_type(ty); + } + ast::ExprKind::Type(ref expr, ref ty) => { + let prec = AssocOp::Colon.precedence() as i8; + self.print_expr_maybe_paren(expr, prec); + self.word_space(":"); + self.print_type(ty); + } + ast::ExprKind::Let(ref pat, ref scrutinee, _) => { + self.print_let(pat, scrutinee); + } + ast::ExprKind::If(ref test, ref blk, ref elseopt) => { + self.print_if(test, blk, elseopt.as_deref()) + } + ast::ExprKind::While(ref test, ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + self.cbox(0); + self.ibox(0); + self.word_nbsp("while"); + self.print_expr_as_cond(test); + self.space(); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + self.cbox(0); + self.ibox(0); + self.word_nbsp("for"); + self.print_pat(pat); + self.space(); + self.word_space("in"); + self.print_expr_as_cond(iter); + self.space(); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::Loop(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + self.cbox(0); + self.ibox(0); + self.word_nbsp("loop"); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::Match(ref expr, ref arms) => { + self.cbox(0); + self.ibox(0); + self.word_nbsp("match"); + self.print_expr_as_cond(expr); + self.space(); + self.bopen(); + self.print_inner_attributes_no_trailing_hardbreak(attrs); + for arm in arms { + self.print_arm(arm); + } + let empty = attrs.is_empty() && arms.is_empty(); + self.bclose(expr.span, empty); + } + ast::ExprKind::Closure( + capture_clause, + asyncness, + movability, + ref decl, + ref body, + _, + ) => { + self.print_movability(movability); + self.print_asyncness(asyncness); + self.print_capture_clause(capture_clause); + + self.print_fn_params_and_ret(decl, true); + self.space(); + self.print_expr(body); + self.end(); // need to close a box + + // a box will be closed by print_expr, but we didn't want an overall + // wrapper so we closed the corresponding opening. so create an + // empty box to satisfy the close. + self.ibox(0); + } + ast::ExprKind::Block(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + // containing cbox, will be closed by print-block at } + self.cbox(0); + // head-box, will be closed by print-block after { + self.ibox(0); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::Async(capture_clause, _, ref blk) => { + self.word_nbsp("async"); + self.print_capture_clause(capture_clause); + // cbox/ibox in analogy to the `ExprKind::Block` arm above + self.cbox(0); + self.ibox(0); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::Await(ref expr) => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); + self.word(".await"); + } + ast::ExprKind::Assign(ref lhs, ref rhs, _) => { + let prec = AssocOp::Assign.precedence() as i8; + self.print_expr_maybe_paren(lhs, prec + 1); + self.space(); + self.word_space("="); + self.print_expr_maybe_paren(rhs, prec); + } + ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => { + let prec = AssocOp::Assign.precedence() as i8; + self.print_expr_maybe_paren(lhs, prec + 1); + self.space(); + self.word(op.node.to_string()); + self.word_space("="); + self.print_expr_maybe_paren(rhs, prec); + } + ast::ExprKind::Field(ref expr, ident) => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); + self.word("."); + self.print_ident(ident); + } + ast::ExprKind::Index(ref expr, ref index) => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); + self.word("["); + self.print_expr(index); + self.word("]"); + } + ast::ExprKind::Range(ref start, ref end, limits) => { + // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence + // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`. + // Here we use a fake precedence value so that any child with lower precedence than + // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) + let fake_prec = AssocOp::LOr.precedence() as i8; + if let Some(ref e) = *start { + self.print_expr_maybe_paren(e, fake_prec); + } + if limits == ast::RangeLimits::HalfOpen { + self.word(".."); + } else { + self.word("..="); + } + if let Some(ref e) = *end { + self.print_expr_maybe_paren(e, fake_prec); + } + } + ast::ExprKind::Underscore => self.word("_"), + ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0), + ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true), + ast::ExprKind::Break(opt_label, ref opt_expr) => { + self.word("break"); + if let Some(label) = opt_label { + self.space(); + self.print_ident(label.ident); + } + if let Some(ref expr) = *opt_expr { + self.space(); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + } + } + ast::ExprKind::Continue(opt_label) => { + self.word("continue"); + if let Some(label) = opt_label { + self.space(); + self.print_ident(label.ident); + } + } + ast::ExprKind::Ret(ref result) => { + self.word("return"); + if let Some(ref expr) = *result { + self.word(" "); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + } + } + ast::ExprKind::InlineAsm(ref a) => { + self.word("asm!"); + self.print_inline_asm(a); + } + ast::ExprKind::MacCall(ref m) => self.print_mac(m), + ast::ExprKind::Paren(ref e) => { + self.popen(); + self.print_expr(e); + self.pclose(); + } + ast::ExprKind::Yield(ref e) => { + self.word("yield"); + + if let Some(ref expr) = *e { + self.space(); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + } + } + ast::ExprKind::Try(ref e) => { + self.print_expr_maybe_paren(e, parser::PREC_POSTFIX); + self.word("?") + } + ast::ExprKind::TryBlock(ref blk) => { + self.cbox(0); + self.ibox(0); + self.word_nbsp("try"); + self.print_block_with_attrs(blk, attrs) + } + ast::ExprKind::Err => { + self.popen(); + self.word("/*ERROR*/"); + self.pclose() + } + } + self.ann.post(self, AnnNode::Expr(expr)); + self.end(); + } + + fn print_arm(&mut self, arm: &ast::Arm) { + // Note, I have no idea why this check is necessary, but here it is. + if arm.attrs.is_empty() { + self.space(); + } + self.cbox(INDENT_UNIT); + self.ibox(0); + self.maybe_print_comment(arm.pat.span.lo()); + self.print_outer_attributes(&arm.attrs); + self.print_pat(&arm.pat); + self.space(); + if let Some(ref e) = arm.guard { + self.word_space("if"); + self.print_expr(e); + self.space(); + } + self.word_space("=>"); + + match arm.body.kind { + ast::ExprKind::Block(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + + // The block will close the pattern's ibox. + self.print_block_unclosed_indent(blk); + + // If it is a user-provided unsafe block, print a comma after it. + if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules { + self.word(","); + } + } + _ => { + self.end(); // Close the ibox for the pattern. + self.print_expr(&arm.body); + self.word(","); + } + } + self.end(); // Close enclosing cbox. + } + + fn print_movability(&mut self, movability: ast::Movability) { + match movability { + ast::Movability::Static => self.word_space("static"), + ast::Movability::Movable => {} + } + } + + fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) { + match capture_clause { + ast::CaptureBy::Value => self.word_space("move"), + ast::CaptureBy::Ref => {} + } + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs new file mode 100644 index 0000000000..d7e9ef0e50 --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -0,0 +1,664 @@ +use crate::pp::Breaks::Inconsistent; +use crate::pprust::state::delimited::IterDelimited; +use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; + +use rustc_ast as ast; +use rustc_ast::GenericBound; +use rustc_ast::ModKind; +use rustc_span::symbol::Ident; + +fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { + format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) +} + +impl<'a> State<'a> { + fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) { + self.print_inner_attributes(attrs); + for item in &nmod.items { + self.print_foreign_item(item); + } + } + + fn print_foreign_item(&mut self, item: &ast::ForeignItem) { + let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; + self.ann.pre(self, AnnNode::SubItem(id)); + self.hardbreak_if_not_bol(); + self.maybe_print_comment(span.lo()); + self.print_outer_attributes(attrs); + match kind { + ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { + self.print_fn_full(sig, ident, generics, vis, *defaultness, 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(box ast::TyAlias { + defaultness, + generics, + bounds, + ty, + }) => { + self.print_associated_type( + ident, + generics, + bounds, + ty.as_deref(), + vis, + *defaultness, + ); + } + ast::ForeignItemKind::MacCall(m) => { + self.print_mac(m); + if m.args.need_semicolon() { + self.word(";"); + } + } + } + self.ann.post(self, AnnNode::SubItem(id)) + } + + fn print_item_const( + &mut self, + ident: Ident, + mutbl: Option, + ty: &ast::Ty, + body: Option<&ast::Expr>, + vis: &ast::Visibility, + defaultness: ast::Defaultness, + ) { + self.head(""); + self.print_visibility(vis); + self.print_defaultness(defaultness); + let leading = match mutbl { + None => "const", + Some(ast::Mutability::Not) => "static", + Some(ast::Mutability::Mut) => "static mut", + }; + self.word_space(leading); + self.print_ident(ident); + self.word_space(":"); + self.print_type(ty); + if body.is_some() { + self.space(); + } + self.end(); // end the head-ibox + if let Some(body) = body { + self.word_space("="); + self.print_expr(body); + } + self.word(";"); + self.end(); // end the outer cbox + } + + fn print_associated_type( + &mut self, + ident: Ident, + generics: &ast::Generics, + bounds: &ast::GenericBounds, + ty: Option<&ast::Ty>, + vis: &ast::Visibility, + defaultness: ast::Defaultness, + ) { + self.head(""); + self.print_visibility(vis); + self.print_defaultness(defaultness); + self.word_space("type"); + self.print_ident(ident); + self.print_generic_params(&generics.params); + self.print_type_bounds(":", bounds); + self.print_where_clause(&generics.where_clause); + if let Some(ty) = ty { + self.space(); + self.word_space("="); + self.print_type(ty); + } + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + + /// Pretty-prints an item. + crate fn print_item(&mut self, item: &ast::Item) { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(item.span.lo()); + self.print_outer_attributes(&item.attrs); + self.ann.pre(self, AnnNode::Item(item)); + match item.kind { + ast::ItemKind::ExternCrate(orig_name) => { + self.head(visibility_qualified(&item.vis, "extern crate")); + if let Some(orig_name) = orig_name { + self.print_name(orig_name); + self.space(); + self.word("as"); + self.space(); + } + self.print_ident(item.ident); + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + ast::ItemKind::Use(ref tree) => { + self.print_visibility(&item.vis); + self.word_nbsp("use"); + self.print_use_tree(tree); + self.word(";"); + } + ast::ItemKind::Static(ref ty, mutbl, ref body) => { + let def = ast::Defaultness::Final; + self.print_item_const(item.ident, Some(mutbl), ty, body.as_deref(), &item.vis, def); + } + 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(box ast::Fn { defaultness, ref sig, ref generics, ref body }) => { + let body = body.as_deref(); + self.print_fn_full( + sig, + item.ident, + generics, + &item.vis, + defaultness, + body, + &item.attrs, + ); + } + ast::ItemKind::Mod(unsafety, ref mod_kind) => { + self.head(Self::to_string(|s| { + s.print_visibility(&item.vis); + s.print_unsafety(unsafety); + s.word("mod"); + })); + self.print_ident(item.ident); + + match mod_kind { + ModKind::Loaded(items, ..) => { + self.nbsp(); + self.bopen(); + self.print_inner_attributes(&item.attrs); + for item in items { + self.print_item(item); + } + let empty = item.attrs.is_empty() && items.is_empty(); + self.bclose(item.span, empty); + } + ModKind::Unloaded => { + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + } + } + ast::ItemKind::ForeignMod(ref nmod) => { + self.head(Self::to_string(|s| { + s.print_unsafety(nmod.unsafety); + s.word("extern"); + })); + if let Some(abi) = nmod.abi { + self.print_literal(&abi.as_lit()); + self.nbsp(); + } + self.bopen(); + self.print_foreign_mod(nmod, &item.attrs); + let empty = item.attrs.is_empty() && nmod.items.is_empty(); + self.bclose(item.span, empty); + } + ast::ItemKind::GlobalAsm(ref asm) => { + self.head(visibility_qualified(&item.vis, "global_asm!")); + self.print_inline_asm(asm); + self.end(); + } + ast::ItemKind::TyAlias(box ast::TyAlias { + defaultness, + ref generics, + ref bounds, + ref ty, + }) => { + let ty = ty.as_deref(); + self.print_associated_type( + item.ident, + generics, + bounds, + ty, + &item.vis, + defaultness, + ); + } + ast::ItemKind::Enum(ref enum_definition, ref params) => { + self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis); + } + ast::ItemKind::Struct(ref struct_def, ref generics) => { + self.head(visibility_qualified(&item.vis, "struct")); + self.print_struct(struct_def, generics, item.ident, item.span, true); + } + ast::ItemKind::Union(ref struct_def, ref generics) => { + self.head(visibility_qualified(&item.vis, "union")); + self.print_struct(struct_def, generics, item.ident, item.span, true); + } + ast::ItemKind::Impl(box ast::Impl { + unsafety, + polarity, + defaultness, + constness, + ref generics, + ref of_trait, + ref self_ty, + ref items, + }) => { + self.head(""); + self.print_visibility(&item.vis); + self.print_defaultness(defaultness); + self.print_unsafety(unsafety); + self.word("impl"); + + if generics.params.is_empty() { + self.nbsp(); + } else { + self.print_generic_params(&generics.params); + self.space(); + } + + self.print_constness(constness); + + if let ast::ImplPolarity::Negative(_) = polarity { + self.word("!"); + } + + if let Some(ref t) = *of_trait { + self.print_trait_ref(t); + self.space(); + self.word_space("for"); + } + + self.print_type(self_ty); + self.print_where_clause(&generics.where_clause); + + self.space(); + self.bopen(); + self.print_inner_attributes(&item.attrs); + for impl_item in items { + self.print_assoc_item(impl_item); + } + let empty = item.attrs.is_empty() && items.is_empty(); + self.bclose(item.span, empty); + } + ast::ItemKind::Trait(box ast::Trait { + is_auto, + unsafety, + ref generics, + ref bounds, + ref items, + .. + }) => { + self.head(""); + self.print_visibility(&item.vis); + self.print_unsafety(unsafety); + self.print_is_auto(is_auto); + self.word_nbsp("trait"); + self.print_ident(item.ident); + self.print_generic_params(&generics.params); + let mut real_bounds = Vec::with_capacity(bounds.len()); + for b in bounds.iter() { + if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b { + self.space(); + self.word_space("for ?"); + self.print_trait_ref(&ptr.trait_ref); + } else { + real_bounds.push(b.clone()); + } + } + self.print_type_bounds(":", &real_bounds); + self.print_where_clause(&generics.where_clause); + self.word(" "); + self.bopen(); + self.print_inner_attributes(&item.attrs); + for trait_item in items { + self.print_assoc_item(trait_item); + } + let empty = item.attrs.is_empty() && items.is_empty(); + self.bclose(item.span, empty); + } + ast::ItemKind::TraitAlias(ref generics, ref bounds) => { + self.head(visibility_qualified(&item.vis, "trait")); + self.print_ident(item.ident); + self.print_generic_params(&generics.params); + let mut real_bounds = Vec::with_capacity(bounds.len()); + // FIXME(durka) this seems to be some quite outdated syntax + for b in bounds.iter() { + if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b { + self.space(); + self.word_space("for ?"); + self.print_trait_ref(&ptr.trait_ref); + } else { + real_bounds.push(b.clone()); + } + } + self.nbsp(); + self.print_type_bounds("=", &real_bounds); + self.print_where_clause(&generics.where_clause); + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + ast::ItemKind::MacCall(ref mac) => { + self.print_mac(mac); + if mac.args.need_semicolon() { + self.word(";"); + } + } + ast::ItemKind::MacroDef(ref macro_def) => { + self.print_mac_def(macro_def, &item.ident, item.span, |state| { + state.print_visibility(&item.vis) + }); + } + } + self.ann.post(self, AnnNode::Item(item)) + } + + fn print_enum_def( + &mut self, + enum_definition: &ast::EnumDef, + generics: &ast::Generics, + ident: Ident, + span: rustc_span::Span, + visibility: &ast::Visibility, + ) { + self.head(visibility_qualified(visibility, "enum")); + self.print_ident(ident); + self.print_generic_params(&generics.params); + self.print_where_clause(&generics.where_clause); + self.space(); + self.print_variants(&enum_definition.variants, span) + } + + fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) { + self.bopen(); + for v in variants { + self.space_if_not_bol(); + self.maybe_print_comment(v.span.lo()); + self.print_outer_attributes(&v.attrs); + self.ibox(0); + self.print_variant(v); + self.word(","); + self.end(); + self.maybe_print_trailing_comment(v.span, None); + } + let empty = variants.is_empty(); + self.bclose(span, empty) + } + + crate fn print_visibility(&mut self, vis: &ast::Visibility) { + match vis.kind { + ast::VisibilityKind::Public => self.word_nbsp("pub"), + ast::VisibilityKind::Crate(sugar) => match sugar { + ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"), + ast::CrateSugar::JustCrate => self.word_nbsp("crate"), + }, + ast::VisibilityKind::Restricted { ref path, .. } => { + let path = Self::to_string(|s| s.print_path(path, false, 0)); + if path == "self" || path == "super" { + self.word_nbsp(format!("pub({})", path)) + } else { + self.word_nbsp(format!("pub(in {})", path)) + } + } + ast::VisibilityKind::Inherited => {} + } + } + + fn print_defaultness(&mut self, defaultness: ast::Defaultness) { + if let ast::Defaultness::Default(_) = defaultness { + self.word_nbsp("default"); + } + } + + fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) { + self.nbsp(); + self.bopen(); + + let empty = fields.is_empty(); + if !empty { + self.hardbreak_if_not_bol(); + + for field in fields { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(field.span.lo()); + self.print_outer_attributes(&field.attrs); + self.print_visibility(&field.vis); + self.print_ident(field.ident.unwrap()); + self.word_nbsp(":"); + self.print_type(&field.ty); + self.word(","); + } + } + + self.bclose(span, empty); + } + + fn print_struct( + &mut self, + struct_def: &ast::VariantData, + generics: &ast::Generics, + ident: Ident, + span: rustc_span::Span, + print_finalizer: bool, + ) { + self.print_ident(ident); + self.print_generic_params(&generics.params); + match struct_def { + ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => { + if let ast::VariantData::Tuple(..) = struct_def { + self.popen(); + self.commasep(Inconsistent, struct_def.fields(), |s, field| { + s.maybe_print_comment(field.span.lo()); + s.print_outer_attributes(&field.attrs); + s.print_visibility(&field.vis); + s.print_type(&field.ty) + }); + self.pclose(); + } + self.print_where_clause(&generics.where_clause); + if print_finalizer { + self.word(";"); + } + self.end(); + self.end(); // Close the outer-box. + } + ast::VariantData::Struct(ref fields, ..) => { + self.print_where_clause(&generics.where_clause); + self.print_record_struct_body(fields, span); + } + } + } + + crate fn print_variant(&mut self, v: &ast::Variant) { + self.head(""); + self.print_visibility(&v.vis); + let generics = ast::Generics::default(); + self.print_struct(&v.data, &generics, v.ident, v.span, false); + if let Some(ref d) = v.disr_expr { + self.space(); + self.word_space("="); + self.print_expr(&d.value) + } + } + + fn print_assoc_item(&mut self, item: &ast::AssocItem) { + let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; + self.ann.pre(self, AnnNode::SubItem(id)); + self.hardbreak_if_not_bol(); + self.maybe_print_comment(span.lo()); + self.print_outer_attributes(attrs); + match kind { + ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { + self.print_fn_full(sig, ident, generics, vis, *defaultness, 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(box ast::TyAlias { defaultness, generics, bounds, ty }) => { + self.print_associated_type( + ident, + generics, + bounds, + ty.as_deref(), + vis, + *defaultness, + ); + } + ast::AssocItemKind::MacCall(m) => { + self.print_mac(m); + if m.args.need_semicolon() { + self.word(";"); + } + } + } + self.ann.post(self, AnnNode::SubItem(id)) + } + + fn print_fn_full( + &mut self, + sig: &ast::FnSig, + name: Ident, + generics: &ast::Generics, + vis: &ast::Visibility, + defaultness: ast::Defaultness, + body: Option<&ast::Block>, + attrs: &[ast::Attribute], + ) { + if body.is_some() { + self.head(""); + } + self.print_visibility(vis); + self.print_defaultness(defaultness); + self.print_fn(&sig.decl, sig.header, Some(name), generics); + if let Some(body) = body { + self.nbsp(); + self.print_block_with_attrs(body, attrs); + } else { + self.word(";"); + } + } + + crate fn print_fn( + &mut self, + decl: &ast::FnDecl, + header: ast::FnHeader, + name: Option, + generics: &ast::Generics, + ) { + self.print_fn_header_info(header); + if let Some(name) = name { + self.nbsp(); + self.print_ident(name); + } + self.print_generic_params(&generics.params); + self.print_fn_params_and_ret(decl, false); + self.print_where_clause(&generics.where_clause) + } + + crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) { + let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") }; + self.word(open); + self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure)); + self.word(close); + self.print_fn_ret_ty(&decl.output) + } + + fn print_where_clause(&mut self, where_clause: &ast::WhereClause) { + if where_clause.predicates.is_empty() && !where_clause.has_where_token { + return; + } + + self.space(); + self.word_space("where"); + + for (i, predicate) in where_clause.predicates.iter().enumerate() { + if i != 0 { + self.word_space(","); + } + + self.print_where_predicate(predicate); + } + } + + pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) { + match predicate { + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + bound_generic_params, + bounded_ty, + bounds, + .. + }) => { + self.print_formal_generic_params(bound_generic_params); + self.print_type(bounded_ty); + self.print_type_bounds(":", bounds); + } + ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + lifetime, + bounds, + .. + }) => { + self.print_lifetime_bounds(*lifetime, bounds); + } + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { + self.print_type(lhs_ty); + self.space(); + self.word_space("="); + self.print_type(rhs_ty); + } + } + } + + fn print_use_tree(&mut self, tree: &ast::UseTree) { + match tree.kind { + ast::UseTreeKind::Simple(rename, ..) => { + self.print_path(&tree.prefix, false, 0); + if let Some(rename) = rename { + self.nbsp(); + self.word_nbsp("as"); + self.print_ident(rename); + } + } + ast::UseTreeKind::Glob => { + if !tree.prefix.segments.is_empty() { + self.print_path(&tree.prefix, false, 0); + self.word("::"); + } + self.word("*"); + } + ast::UseTreeKind::Nested(ref items) => { + if !tree.prefix.segments.is_empty() { + self.print_path(&tree.prefix, false, 0); + self.word("::"); + } + if items.is_empty() { + self.word("{}"); + } else if items.len() == 1 { + self.print_use_tree(&items[0].0); + } else { + self.cbox(INDENT_UNIT); + self.word("{"); + self.zerobreak(); + self.ibox(0); + for use_tree in items.iter().delimited() { + self.print_use_tree(&use_tree.0); + if !use_tree.is_last { + self.word(","); + if let ast::UseTreeKind::Nested(_) = use_tree.0.kind { + self.hardbreak(); + } else { + self.space(); + } + } + } + self.end(); + self.trailing_comma(); + self.offset(-INDENT_UNIT); + self.word("}"); + self.end(); + } + } + } + } +} diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index bab50df3dd..49043e9f5f 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -1,10 +1,13 @@ //! Parsing and validation of builtin attributes -use rustc_ast::{self as ast, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem}; +use rustc_ast as ast; +use rustc_ast::node_id::CRATE_NODE_ID; +use rustc_ast::{Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability}; use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg}; use rustc_macros::HashStable_Generic; +use rustc_session::lint::builtin::UNEXPECTED_CFGS; use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::Session; use rustc_span::hygiene::Transparency; @@ -217,85 +220,81 @@ where let mut issue_num = None; let mut is_soft = false; for meta in metas { - if let Some(mi) = meta.meta_item() { - match mi.name_or_empty() { - sym::feature => { - if !get(mi, &mut feature) { - continue 'outer; - } + let Some(mi) = meta.meta_item() else { + handle_errors( + &sess.parse_sess, + meta.span(), + AttrError::UnsupportedLiteral("unsupported literal", false), + ); + continue 'outer; + }; + match mi.name_or_empty() { + sym::feature => { + if !get(mi, &mut feature) { + continue 'outer; } - sym::reason => { - if !get(mi, &mut reason) { - continue 'outer; - } + } + sym::reason => { + if !get(mi, &mut reason) { + continue 'outer; + } + } + sym::issue => { + if !get(mi, &mut issue) { + continue 'outer; } - sym::issue => { - if !get(mi, &mut issue) { - continue 'outer; - } - // These unwraps are safe because `get` ensures the meta item - // is a name/value pair string literal. - issue_num = match issue.unwrap().as_str() { - "none" => None, - issue => { - let emit_diag = |msg: &str| { - struct_span_err!( - diagnostic, - mi.span, - E0545, - "`issue` must be a non-zero numeric string \ - or \"none\"", - ) - .span_label( - mi.name_value_literal_span().unwrap(), - msg, - ) - .emit(); - }; - match issue.parse() { - Ok(0) => { - emit_diag( - "`issue` must not be \"0\", \ - use \"none\" instead", - ); - continue 'outer; - } - Ok(num) => NonZeroU32::new(num), - Err(err) => { - emit_diag(&err.to_string()); - continue 'outer; - } + // These unwraps are safe because `get` ensures the meta item + // is a name/value pair string literal. + issue_num = match issue.unwrap().as_str() { + "none" => None, + issue => { + let emit_diag = |msg: &str| { + struct_span_err!( + diagnostic, + mi.span, + E0545, + "`issue` must be a non-zero numeric string \ + or \"none\"", + ) + .span_label(mi.name_value_literal_span().unwrap(), msg) + .emit(); + }; + match issue.parse() { + Ok(0) => { + emit_diag( + "`issue` must not be \"0\", \ + use \"none\" instead", + ); + continue 'outer; + } + Ok(num) => NonZeroU32::new(num), + Err(err) => { + emit_diag(&err.to_string()); + continue 'outer; } } - }; - } - sym::soft => { - if !mi.is_word() { - let msg = "`soft` should not have any arguments"; - sess.parse_sess.span_diagnostic.span_err(mi.span, msg); } - is_soft = true; - } - _ => { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnknownMetaItem( - pprust::path_to_string(&mi.path), - &["feature", "reason", "issue", "soft"], - ), - ); - continue 'outer; + }; + } + sym::soft => { + if !mi.is_word() { + let msg = "`soft` should not have any arguments"; + sess.parse_sess.span_diagnostic.span_err(mi.span, msg); } + is_soft = true; + } + _ => { + handle_errors( + &sess.parse_sess, + meta.span(), + AttrError::UnknownMetaItem( + pprust::path_to_string(&mi.path), + &["feature", "reason", "issue", "soft"], + ), + ); + continue 'outer; } - } else { - handle_errors( - &sess.parse_sess, - meta.span(), - AttrError::UnsupportedLiteral("unsupported literal", false), - ); - continue 'outer; } } @@ -462,8 +461,30 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat true } MetaItemKind::NameValue(..) | MetaItemKind::Word => { - let ident = cfg.ident().expect("multi-segment cfg predicate"); - sess.config.contains(&(ident.name, cfg.value_str())) + let name = cfg.ident().expect("multi-segment cfg predicate").name; + let value = cfg.value_str(); + if sess.check_config.names_checked && !sess.check_config.names_valid.contains(&name) + { + sess.buffer_lint( + UNEXPECTED_CFGS, + cfg.span, + CRATE_NODE_ID, + "unexpected `cfg` condition name", + ); + } + if let Some(val) = value { + if sess.check_config.values_checked.contains(&name) + && !sess.check_config.values_valid.contains(&(name, val)) + { + sess.buffer_lint( + UNEXPECTED_CFGS, + cfg.span, + CRATE_NODE_ID, + "unexpected `cfg` condition value", + ); + } + } + sess.config.contains(&(name, value)) } } }) @@ -608,7 +629,7 @@ pub fn eval_condition( } } -#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic)] +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] pub struct Deprecation { pub since: Option, /// The note to issue a reason. diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index 3fb11f7787..c95c1c40a3 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -4,6 +4,8 @@ //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! to this crate. +#![feature(let_else)] + #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 75e9c69af4..eb2fdbd079 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -8,7 +8,7 @@ doctest = false [dependencies] either = "1.5.0" -itertools = "0.9" +itertools = "0.10" tracing = "0.1" polonius-engine = "0.13.0" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 5702203d7c..7140cda8e4 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -327,7 +327,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { verb: &str, optional_adverb_for_moved: &str, moved_path: Option, - ) -> DiagnosticBuilder<'cx> { + ) -> DiagnosticBuilder<'tcx> { let moved_path = moved_path.map(|mp| format!(": `{}`", mp)).unwrap_or_default(); struct_span_err!( diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs index a40f148cdf..22edee33c5 100644 --- a/compiler/rustc_borrowck/src/constraint_generation.rs +++ b/compiler/rustc_borrowck/src/constraint_generation.rs @@ -60,8 +60,8 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> { /// We sometimes have `region` within an rvalue, or within a /// call. Make them live at the location where they appear. - fn visit_region(&mut self, region: &ty::Region<'tcx>, location: Location) { - self.add_regular_live_constraint(*region, location); + fn visit_region(&mut self, region: ty::Region<'tcx>, location: Location) { + self.add_regular_live_constraint(region, location); self.super_region(region); } diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index 98378a9868..d41143ee76 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -72,7 +72,7 @@ impl<'tcx> Index for OutlivesConstraintSet<'tcx> { } } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, PartialEq, Eq)] pub struct OutlivesConstraint<'tcx> { // NB. The ordering here is not significant for correctness, but // it is for convenience. Before we dump the constraints in the diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 15372ec153..f0036f09c3 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -8,7 +8,6 @@ use rustc_mir_dataflow::ResultsVisitable; use rustc_mir_dataflow::{self, fmt::DebugWithContext, CallReturnPlaces, GenKill}; use rustc_mir_dataflow::{Analysis, Direction, Results}; use std::fmt; -use std::iter; use crate::{ places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid, @@ -385,14 +384,6 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { self.kill_borrows_on_place(trans, Place::from(local)); } - mir::StatementKind::LlvmInlineAsm(ref asm) => { - for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) { - if !kind.is_indirect && !kind.is_rw { - self.kill_borrows_on_place(trans, *output); - } - } - } - mir::StatementKind::FakeRead(..) | mir::StatementKind::SetDiscriminant { .. } | mir::StatementKind::StorageLive(..) diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index 70acbc9ee2..eec994f88b 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -16,9 +16,6 @@ pub fn categorize(context: PlaceContext) -> Option { PlaceContext::MutatingUse(MutatingUseContext::Store) | - // This is potentially both a def and a use... - PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput) | - // We let Call define the result in both the success and // unwind cases. This is not really correct, however it // does not seem to be observable due to the way that we diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 96326ef2d5..2a906e41b8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -55,7 +55,7 @@ impl<'tcx> UniverseInfo<'tcx> { found, TypeError::RegionsPlaceholderMismatch, ); - err.buffer(&mut mbcx.errors_buffer); + mbcx.buffer_error(err); } UniverseInfoInner::TypeOp(ref type_op_info) => { type_op_info.report_error(mbcx, placeholder, error_element, cause); @@ -64,11 +64,9 @@ impl<'tcx> UniverseInfo<'tcx> { // FIXME: This error message isn't great, but it doesn't show // up in the existing UI tests. Consider investigating this // some more. - mbcx.infcx - .tcx - .sess - .struct_span_err(cause.span, "higher-ranked subtype error") - .buffer(&mut mbcx.errors_buffer); + mbcx.buffer_error( + mbcx.infcx.tcx.sess.struct_span_err(cause.span, "higher-ranked subtype error"), + ); } } } @@ -144,12 +142,10 @@ trait TypeOpInfo<'tcx> { let tcx = mbcx.infcx.tcx; let base_universe = self.base_universe(); - let adjusted_universe = if let Some(adjusted) = + let Some(adjusted_universe) = placeholder.universe.as_u32().checked_sub(base_universe.as_u32()) - { - adjusted - } else { - self.fallback_error(tcx, cause.span).buffer(&mut mbcx.errors_buffer); + else { + mbcx.buffer_error(self.fallback_error(tcx, cause.span)); return; }; @@ -178,9 +174,9 @@ trait TypeOpInfo<'tcx> { let nice_error = self.nice_error(tcx, cause, placeholder_region, error_region); if let Some(nice_error) = nice_error { - nice_error.buffer(&mut mbcx.errors_buffer); + mbcx.buffer_error(nice_error); } else { - self.fallback_error(tcx, span).buffer(&mut mbcx.errors_buffer); + mbcx.buffer_error(self.fallback_error(tcx, span)); } } } @@ -358,8 +354,8 @@ fn try_extract_error_from_fulfill_cx<'tcx>( })?; debug!(?sub_region, "cause = {:#?}", cause); - let nice_error = match (error_region, sub_region) { - (Some(error_region), &ty::ReVar(vid)) => NiceRegionError::new( + let nice_error = match (error_region, *sub_region) { + (Some(error_region), ty::ReVar(vid)) => NiceRegionError::new( infcx, RegionResolutionError::SubSupConflict( vid, @@ -376,7 +372,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>( RegionResolutionError::ConcreteFailure(cause.clone(), error_region, placeholder_region), ), // Note universe here is wrong... - (None, &ty::ReVar(vid)) => NiceRegionError::new( + (None, ty::ReVar(vid)) => NiceRegionError::new( infcx, RegionResolutionError::UpperBoundUniverseConflict( vid, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index a24b7cff9e..fce5ed0ef4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1,4 +1,5 @@ use either::Either; +use rustc_const_eval::util::{CallDesugaringKind, CallKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -26,7 +27,7 @@ use crate::{ use super::{ explain_borrow::{BorrowExplanation, LaterUseKind}, - FnSelfUseKind, IncludingDowncast, RegionName, RegionNameSource, UseSpans, + IncludingDowncast, RegionName, RegionNameSource, UseSpans, }; #[derive(Debug)] @@ -104,9 +105,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), ); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } else { - if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) { + if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) { if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) { debug!( "report_use_of_moved_or_uninitialized place: error suppressed \ @@ -195,7 +196,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .map(|n| format!("`{}`", n)) .unwrap_or_else(|| "value".to_owned()); match kind { - FnSelfUseKind::FnOnceCall => { + CallKind::FnCall { fn_trait_id, .. } + if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() => + { err.span_label( fn_call_span, &format!( @@ -208,7 +211,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "this value implements `FnOnce`, which causes it to be moved when called", ); } - FnSelfUseKind::Operator { self_arg } => { + CallKind::Operator { self_arg, .. } => { + let self_arg = self_arg.unwrap(); err.span_label( fn_call_span, &format!( @@ -235,12 +239,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } - FnSelfUseKind::Normal { - self_arg, - implicit_into_iter, - is_option_or_result, - } => { - if implicit_into_iter { + CallKind::Normal { self_arg, desugaring, is_option_or_result } => { + let self_arg = self_arg.unwrap(); + if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { err.span_label( fn_call_span, &format!( @@ -305,8 +306,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } - // Deref::deref takes &self, which cannot cause a move - FnSelfUseKind::DerefCoercion { .. } => unreachable!(), + // Other desugarings takes &self, which cannot cause a move + _ => unreachable!(), } } else { err.span_label( @@ -409,8 +410,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let generics = tcx.generics_of(self.mir_def_id()); let param = generics.type_param(¶m_ty, tcx); if let Some(generics) = tcx - .hir() - .get_generics(tcx.typeck_root_def_id(self.mir_def_id().to_def_id())) + .typeck_root_def_id(self.mir_def_id().to_def_id()) + .as_local() + .and_then(|def_id| tcx.hir().get_generics(def_id)) { suggest_constraining_type_param( tcx, @@ -432,7 +434,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } if let UseSpans::FnSelfUse { - kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty }, + kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. }, .. } = use_spans { @@ -448,12 +450,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - if let Some((_, mut old_err)) = - self.move_error_reported.insert(move_out_indices, (used_place, err)) - { - // Cancel the old error so it doesn't ICE. - old_err.cancel(); - } + self.buffer_move_error(move_out_indices, (used_place, err)); } } @@ -502,7 +499,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(borrow_span), None, ); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } pub(crate) fn report_use_while_mutably_borrowed( @@ -1020,7 +1017,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if self.body.local_decls[borrowed_local].is_ref_to_thread_local() { let err = self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); return; } @@ -1112,7 +1109,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ), }; - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } fn report_local_value_does_not_live_long_enough( @@ -1294,7 +1291,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } fn report_thread_local_value_does_not_live_long_enough( @@ -1809,7 +1806,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { loan.kind.describe_mutability(), ); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); return; } @@ -1835,7 +1832,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.explain_deref_coercion(loan, &mut err); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) { @@ -1937,7 +1934,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } err.span_label(span, msg); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> { @@ -2327,7 +2324,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // This is also case 2 from above but for functions, return type is still an // anonymous reference so we select the first argument. let argument_span = fn_decl.inputs.first()?.span; - let argument_ty = sig.inputs().skip_binder().first()?; + let argument_ty = *sig.inputs().skip_binder().first()?; let return_span = fn_decl.output.span(); let return_ty = sig.output().skip_binder(); @@ -2382,27 +2379,27 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { diag: &mut DiagnosticBuilder<'_>, ) -> String { match self { - AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { + &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { diag.span_label( - *argument_span, + argument_span, format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)), ); cx.get_region_name_for_ty(argument_ty, 0) } - AnnotatedBorrowFnSignature::AnonymousFunction { + &AnnotatedBorrowFnSignature::AnonymousFunction { argument_ty, argument_span, return_ty, return_span, } => { let argument_ty_name = cx.get_name_for_ty(argument_ty, 0); - diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name)); + diag.span_label(argument_span, format!("has type `{}`", argument_ty_name)); let return_ty_name = cx.get_name_for_ty(return_ty, 0); let types_equal = return_ty_name == argument_ty_name; diag.span_label( - *return_span, + return_span, format!( "{}has type `{}`", if types_equal { "also " } else { "" }, @@ -2422,7 +2419,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { } AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => { // Region of return type and arguments checked to be the same earlier. - let region_name = cx.get_region_name_for_ty(return_ty, 0); + let region_name = cx.get_region_name_for_ty(*return_ty, 0); for (_, argument_span) in arguments { diag.span_label(*argument_span, format!("has lifetime `{}`", region_name)); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index e2eb125981..73b0d39828 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1,10 +1,10 @@ //! Borrow checker diagnostics. +use rustc_const_eval::util::call_kind; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItemGroup; use rustc_hir::GeneratorKind; use rustc_middle::mir::{ AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand, @@ -13,7 +13,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::print::Print; use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; -use rustc_span::{hygiene::DesugaringKind, symbol::sym, Span}; +use rustc_span::{symbol::sym, Span}; use rustc_target::abi::VariantIdx; use super::borrow_set::BorrowData; @@ -37,7 +37,7 @@ crate use mutability_errors::AccessKind; crate use outlives_suggestion::OutlivesSuggestionBuilder; crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; crate use region_name::{RegionName, RegionNameSource}; -use rustc_span::symbol::Ident; +crate use rustc_const_eval::util::CallKind; pub(super) struct IncludingDowncast(pub(super) bool); @@ -331,7 +331,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { match place { PlaceRef { local, projection: [] } => { let local = &self.body.local_decls[local]; - self.describe_field_from_ty(&local.ty, field, None) + self.describe_field_from_ty(local.ty, field, None) } PlaceRef { local, projection: [proj_base @ .., elem] } => match elem { ProjectionElem::Deref => { @@ -339,10 +339,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } ProjectionElem::Downcast(_, variant_index) => { let base_ty = place.ty(self.body, self.infcx.tcx).ty; - self.describe_field_from_ty(&base_ty, field, Some(*variant_index)) + self.describe_field_from_ty(base_ty, field, Some(*variant_index)) } ProjectionElem::Field(_, field_type) => { - self.describe_field_from_ty(&field_type, field, None) + self.describe_field_from_ty(*field_type, field, None) } ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } @@ -362,7 +362,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> String { if ty.is_box() { // If the type is a box, the field is described from the boxed type - self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index) + self.describe_field_from_ty(ty.boxed_ty(), field, variant_index) } else { match *ty.kind() { ty::Adt(def, _) => { @@ -372,14 +372,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { def.non_enum_variant() }; - variant.fields[field.index()].ident.to_string() + variant.fields[field.index()].name.to_string() } ty::Tuple(_) => field.index().to_string(), ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { - self.describe_field_from_ty(&ty, field, variant_index) + self.describe_field_from_ty(ty, field, variant_index) } ty::Array(ty, _) | ty::Slice(ty) => { - self.describe_field_from_ty(&ty, field, variant_index) + self.describe_field_from_ty(ty, field, variant_index) } ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { // We won't be borrowck'ing here if the closure came from another crate, @@ -497,14 +497,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // We need to add synthesized lifetimes where appropriate. We do // this by hooking into the pretty printer and telling it to label the // lifetimes without names with the value `'0`. - match ty.kind() { - ty::Ref( - ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. }) - | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), - _, - _, - ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter), - _ => {} + if let ty::Ref(region, ..) = ty.kind() { + match **region { + ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) + | ty::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { + printer.region_highlight_mode.highlighting_bound_region(br, counter) + } + _ => {} + } } let _ = ty.print(printer); @@ -517,19 +517,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut s = String::new(); let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); - let region = match ty.kind() { - ty::Ref(region, _, _) => { - match region { - ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. }) - | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { - printer.region_highlight_mode.highlighting_bound_region(*br, counter) - } - _ => {} + let region = if let ty::Ref(region, ..) = ty.kind() { + match **region { + ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) + | ty::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { + printer.region_highlight_mode.highlighting_bound_region(br, counter) } - - region + _ => {} } - _ => bug!("ty for annotation of borrow region is not a reference"), + region + } else { + bug!("ty for annotation of borrow region is not a reference"); }; let _ = region.print(printer); @@ -563,7 +561,7 @@ pub(super) enum UseSpans<'tcx> { fn_call_span: Span, /// The definition span of the method being called fn_span: Span, - kind: FnSelfUseKind<'tcx>, + kind: CallKind<'tcx>, }, /// This access is caused by a `match` or `if let` pattern. PatUse(Span), @@ -571,38 +569,15 @@ pub(super) enum UseSpans<'tcx> { OtherUse(Span), } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum FnSelfUseKind<'tcx> { - /// A normal method call of the form `receiver.foo(a, b, c)` - Normal { - self_arg: Ident, - implicit_into_iter: bool, - /// Whether the self type of the method call has an `.as_ref()` method. - /// Used for better diagnostics. - is_option_or_result: bool, - }, - /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` - FnOnceCall, - /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) - Operator { self_arg: Ident }, - DerefCoercion { - /// The `Span` of the `Target` associated type - /// in the `Deref` impl we are using. - deref_target: Span, - /// The type `T::Deref` we are dereferencing to - deref_target_ty: Ty<'tcx>, - }, -} - impl UseSpans<'_> { pub(super) fn args_or_use(self) -> Span { match self { UseSpans::ClosureUse { args_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -613,9 +588,9 @@ impl UseSpans<'_> { UseSpans::ClosureUse { path_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -626,9 +601,9 @@ impl UseSpans<'_> { UseSpans::ClosureUse { capture_kind_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -892,79 +867,30 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, .. }) = &self.body[location.block].terminator { - let (method_did, method_substs) = if let Some(info) = + let Some((method_did, method_substs)) = rustc_const_eval::util::find_self_call( self.infcx.tcx, &self.body, target_temp, location.block, - ) { - info - } else { + ) + else { return normal_ret; }; - let tcx = self.infcx.tcx; - let parent = tcx.parent(method_did); - let is_fn_once = parent == tcx.lang_items().fn_once_trait(); - let is_operator = !from_hir_call - && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p)); - let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); - let fn_call_span = *fn_span; - - let self_arg = tcx.fn_arg_names(method_did)[0]; - - debug!( - "terminator = {:?} from_hir_call={:?}", - self.body[location.block].terminator, from_hir_call + let kind = call_kind( + self.infcx.tcx, + self.param_env, + method_did, + method_substs, + *fn_span, + *from_hir_call, + Some(self.infcx.tcx.fn_arg_names(method_did)[0]), ); - // Check for a 'special' use of 'self' - - // an FnOnce call, an operator (e.g. `<<`), or a - // deref coercion. - let kind = if is_fn_once { - Some(FnSelfUseKind::FnOnceCall) - } else if is_operator { - Some(FnSelfUseKind::Operator { self_arg }) - } else if is_deref { - let deref_target = - tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { - Instance::resolve(tcx, self.param_env, deref_target, method_substs) - .transpose() - }); - if let Some(Ok(instance)) = deref_target { - let deref_target_ty = instance.ty(tcx, self.param_env); - Some(FnSelfUseKind::DerefCoercion { - deref_target: tcx.def_span(instance.def_id()), - deref_target_ty, - }) - } else { - None - } - } else { - None - }; - - let kind = kind.unwrap_or_else(|| { - // This isn't a 'special' use of `self` - debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span); - let implicit_into_iter = Some(method_did) == tcx.lang_items().into_iter_fn() - && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop); - let parent_self_ty = parent - .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) - .and_then(|did| match tcx.type_of(did).kind() { - ty::Adt(def, ..) => Some(def.did), - _ => None, - }); - let is_option_or_result = parent_self_ty.map_or(false, |def_id| { - matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) - }); - FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result } - }); - return FnSelfUse { var_span: stmt.source_info.span, - fn_call_span, + fn_call_span: *fn_span, fn_span: self .infcx .tcx diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 692c20d7df..cc6566882a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1,3 +1,4 @@ +use rustc_const_eval::util::CallDesugaringKind; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; @@ -8,7 +9,7 @@ use rustc_mir_dataflow::move_paths::{ use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; -use crate::diagnostics::{FnSelfUseKind, UseSpans}; +use crate::diagnostics::{CallKind, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; @@ -245,18 +246,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); ( match kind { - IllegalMoveOriginKind::BorrowedContent { target_place } => self + &IllegalMoveOriginKind::BorrowedContent { target_place } => self .report_cannot_move_from_borrowed_content( original_path, - *target_place, + target_place, span, use_spans, ), - IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { + &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { self.cannot_move_out_of_interior_of_drop(span, ty) } - IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { - self.cannot_move_out_of_interior_noncopy(span, ty, Some(*is_index)) + &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { + self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index)) } }, span, @@ -264,7 +265,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; self.add_move_hints(error, &mut err, err_span); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } fn report_cannot_move_from_static( @@ -410,7 +411,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } else if let Some(UseSpans::FnSelfUse { - kind: FnSelfUseKind::Normal { implicit_into_iter: true, .. }, + kind: + CallKind::Normal { desugaring: Some((CallDesugaringKind::ForLoopIntoIter, _)), .. }, .. }) = use_spans { diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index b5dad5ccde..7d0dde53c2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -488,12 +488,32 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // don't create labels for compiler-generated spans Some(_) => None, None => { - let (span, suggestion) = suggest_ampmut( - self.infcx.tcx, - local_decl, - opt_assignment_rhs_span, - *opt_ty_info, - ); + let (span, suggestion) = if name != kw::SelfLower { + suggest_ampmut( + self.infcx.tcx, + local_decl, + opt_assignment_rhs_span, + *opt_ty_info, + ) + } else { + match local_decl.local_info.as_deref() { + Some(LocalInfo::User(ClearCrossCrate::Set( + mir::BindingForm::Var(mir::VarBindingForm { + opt_ty_info: None, + .. + }), + ))) => { + suggest_ampmut_self(self.infcx.tcx, local_decl) + } + // explicit self (eg `self: &'a Self`) + _ => suggest_ampmut( + self.infcx.tcx, + local_decl, + opt_assignment_rhs_span, + *opt_ty_info, + ), + } + }; Some((true, span, suggestion)) } } @@ -606,7 +626,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } /// User cannot make signature of a trait mutable without changing the @@ -619,51 +639,46 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let hir_map = self.infcx.tcx.hir(); let my_def = self.body.source.def_id(); let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap()); - let td = if let Some(a) = + let Some(td) = self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x)) - { - a - } else { + else { return (false, None); }; ( true, - td.as_local().and_then(|tld| { - let h = hir_map.local_def_id_to_hir_id(tld); - match hir_map.find(h) { - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, _, _, items), - .. - })) => { - let mut f_in_trait_opt = None; - for hir::TraitItemRef { id: fi, kind: k, .. } in *items { - let hi = fi.hir_id(); - if !matches!(k, hir::AssocItemKind::Fn { .. }) { - continue; - } - if hir_map.name(hi) != hir_map.name(my_hir) { - continue; - } - f_in_trait_opt = Some(hi); - break; + td.as_local().and_then(|tld| match hir_map.find_by_def_id(tld) { + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, _, _, items), + .. + })) => { + let mut f_in_trait_opt = None; + for hir::TraitItemRef { id: fi, kind: k, .. } in *items { + let hi = fi.hir_id(); + if !matches!(k, hir::AssocItemKind::Fn { .. }) { + continue; } - f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) { - Some(Node::TraitItem(hir::TraitItem { - kind: - hir::TraitItemKind::Fn( - hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. }, - _, - ), - .. - })) => { - let hir::Ty { span, .. } = inputs[local.index() - 1]; - Some(span) - } - _ => None, - }) + if hir_map.name(hi) != hir_map.name(my_hir) { + continue; + } + f_in_trait_opt = Some(hi); + break; } - _ => None, + f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) { + Some(Node::TraitItem(hir::TraitItem { + kind: + hir::TraitItemKind::Fn( + hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. }, + _, + ), + .. + })) => { + let hir::Ty { span, .. } = inputs[local.index() - 1]; + Some(span) + } + _ => None, + }) } + _ => None, }), ) } @@ -706,13 +721,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { &origin_projection, ) { match captured_place.info.capture_kind { - ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, - .. - }) => { + ty::UpvarCapture::ByRef( + ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, + ) => { capture_reason = format!("mutable borrow of `{}`", upvar); } - ty::UpvarCapture::ByValue(_) => { + ty::UpvarCapture::ByValue => { capture_reason = format!("possible mutation of `{}`", upvar); } _ => bug!("upvar `{}` borrowed, but not mutably", upvar), @@ -747,7 +761,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { HirId, ImplItem, ImplItemKind, Item, ItemKind, }; - fn maybe_body_id_of_fn(hir_map: &Map<'_>, id: HirId) -> Option { + fn maybe_body_id_of_fn(hir_map: Map<'_>, id: HirId) -> Option { match hir_map.find(id) { Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. })) | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => { @@ -758,7 +772,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } let hir_map = self.infcx.tcx.hir(); let mir_body_hir_id = self.mir_hir_id(); - if let Some(fn_body_id) = maybe_body_id_of_fn(&hir_map, mir_body_hir_id) { + if let Some(fn_body_id) = maybe_body_id_of_fn(hir_map, mir_body_hir_id) { if let Block( hir::Block { expr: @@ -802,7 +816,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .map(|assoc_items| { assoc_items .in_definition_order() - .map(|assoc_item_def| assoc_item_def.ident) + .map(|assoc_item_def| assoc_item_def.ident(self.infcx.tcx)) .filter(|&ident| { let original_method_ident = path_segment.ident; original_method_ident != ident @@ -897,7 +911,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if look_at_return && hir.get_return_block(closure_id).is_some() { // ...otherwise we are probably in the tail expression of the function, point at the // return type. - match hir.get(hir.get_parent_item(fn_call_id)) { + match hir.get_by_def_id(hir.get_parent_item(fn_call_id)) { hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }) | hir::Node::TraitItem(hir::TraitItem { ident, @@ -1076,8 +1090,7 @@ fn get_mut_span_in_struct_field<'tcx>( if let ty::Adt(def, _) = ty.kind() { let field = def.all_fields().nth(field.index())?; // Use the HIR types to construct the diagnostic message. - let hir_id = tcx.hir().local_def_id_to_hir_id(field.did.as_local()?); - let node = tcx.hir().find(hir_id)?; + let node = tcx.hir().find_by_def_id(field.did.as_local()?)?; // Now we're dealing with the actual struct that we're going to suggest a change to, // we can expect a field that is an immutable reference to a type. if let hir::Node::Field(field) = node { diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index 723b57ed97..74e872e200 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -256,6 +256,6 @@ impl OutlivesSuggestionBuilder { diag.sort_span = mir_span.shrink_to_hi(); // Buffer the diagnostic - diag.buffer(&mut mbcx.errors_buffer); + mbcx.buffer_non_error_diag(diag); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index df23eaf24b..e6a323d676 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -5,6 +5,7 @@ use rustc_infer::infer::{ error_reporting::nice_region_error::NiceRegionError, error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin, }; +use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::{self, RegionVid, Ty}; @@ -139,7 +140,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// Returns `true` if a closure is inferred to be an `FnMut` closure. fn is_closure_fn_mut(&self, fr: RegionVid) -> bool { - if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) { + if let Some(ty::ReFree(free_region)) = self.to_error_region(fr).as_deref() { if let ty::BoundRegionKind::BrEnv = free_region.bound_region { if let DefiningTy::Closure(_, substs) = self.regioncx.universal_regions().defining_ty @@ -168,14 +169,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let type_test_span = type_test.locations.span(&self.body); if let Some(lower_bound_region) = lower_bound_region { - self.infcx - .construct_generic_bound_failure( - type_test_span, - None, - type_test.generic_kind, - lower_bound_region, - ) - .buffer(&mut self.errors_buffer); + self.buffer_error(self.infcx.construct_generic_bound_failure( + type_test_span, + None, + type_test.generic_kind, + lower_bound_region, + )); } else { // FIXME. We should handle this case better. It // indicates that we have e.g., some region variable @@ -186,27 +185,22 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // to report it; we could probably handle it by // iterating over the universal regions and reporting // an error that multiple bounds are required. - self.infcx - .tcx - .sess - .struct_span_err( - type_test_span, - &format!("`{}` does not live long enough", type_test.generic_kind), - ) - .buffer(&mut self.errors_buffer); + self.buffer_error(self.infcx.tcx.sess.struct_span_err( + type_test_span, + &format!("`{}` does not live long enough", type_test.generic_kind), + )); } } RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, member_region } => { let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); - unexpected_hidden_region_diagnostic( + self.buffer_error(unexpected_hidden_region_diagnostic( self.infcx.tcx, span, named_ty, named_region, - ) - .buffer(&mut self.errors_buffer); + )); } RegionErrorKind::BoundUniversalRegionError { @@ -285,7 +279,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { let nice = NiceRegionError::new_from_span(self.infcx, cause.span, o, f); if let Some(diag) = nice.try_report_from_nll() { - diag.buffer(&mut self.errors_buffer); + self.buffer_error(diag); return; } } @@ -375,7 +369,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - diag.buffer(&mut self.errors_buffer); + self.buffer_error(diag); } /// Report a specialized error when `FnMut` closures return a reference to a captured variable. @@ -428,17 +422,26 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { diag.span_label(*span, message); - // FIXME(project-rfc-2229#48): This should store a captured_place not a hir id - if let ReturnConstraint::ClosureUpvar(upvar) = kind { + if let ReturnConstraint::ClosureUpvar(upvar_field) = kind { let def_id = match self.regioncx.universal_regions().defining_ty { DefiningTy::Closure(def_id, _) => def_id, ty => bug!("unexpected DefiningTy {:?}", ty), }; - let upvar_def_span = self.infcx.tcx.hir().span(upvar); - let upvar_span = self.infcx.tcx.upvars_mentioned(def_id).unwrap()[&upvar].span; - diag.span_label(upvar_def_span, "variable defined here"); - diag.span_label(upvar_span, "variable captured here"); + let captured_place = &self.upvars[upvar_field.index()].place; + let defined_hir = match captured_place.place.base { + PlaceBase::Local(hirid) => Some(hirid), + PlaceBase::Upvar(upvar) => Some(upvar.var_path.hir_id), + _ => None, + }; + + if defined_hir.is_some() { + let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap(); + let upvar_def_span = self.infcx.tcx.hir().span(defined_hir.unwrap()); + let upvar_span = upvars_map.get(&defined_hir.unwrap()).unwrap().span; + diag.span_label(upvar_def_span, "variable defined here"); + diag.span_label(upvar_span, "variable captured here"); + } } if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() { @@ -635,8 +638,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fr_name: RegionName, outlived_fr: RegionVid, ) { - if let (Some(f), Some(ty::RegionKind::ReStatic)) = - (self.to_error_region(fr), self.to_error_region(outlived_fr)) + if let (Some(f), Some(ty::ReStatic)) = + (self.to_error_region(fr), self.to_error_region(outlived_fr).as_deref()) { if let Some(&ty::Opaque(did, substs)) = self .infcx @@ -659,7 +662,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { bound.kind().skip_binder() { let r = r.subst(self.infcx.tcx, substs); - if let ty::RegionKind::ReStatic = r { + if r.is_static() { found = true; break; } else { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 80f5f77a02..3bcc9f7be3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -264,7 +264,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let tcx = self.infcx.tcx; debug!("give_region_a_name: error_region = {:?}", error_region); - match error_region { + match *error_region { ty::ReEarlyBound(ebr) => { if ebr.has_name() { let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP); @@ -311,43 +311,39 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::BoundRegionKind::BrEnv => { let def_ty = self.regioncx.universal_regions().defining_ty; - if let DefiningTy::Closure(_, substs) = def_ty { - let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) = - tcx.hir().expect_expr(self.mir_hir_id()).kind - { - span - } else { - bug!("Closure is not defined by a closure expr"); - }; - let region_name = self.synthesize_region_name(); - - let closure_kind_ty = substs.as_closure().kind_ty(); - let note = match closure_kind_ty.to_opt_closure_kind() { - Some(ty::ClosureKind::Fn) => { - "closure implements `Fn`, so references to captured variables \ - can't escape the closure" - } - Some(ty::ClosureKind::FnMut) => { - "closure implements `FnMut`, so references to captured variables \ - can't escape the closure" - } - Some(ty::ClosureKind::FnOnce) => { - bug!("BrEnv in a `FnOnce` closure"); - } - None => bug!("Closure kind not inferred in borrow check"), - }; - - Some(RegionName { - name: region_name, - source: RegionNameSource::SynthesizedFreeEnvRegion( - args_span, - note.to_string(), - ), - }) - } else { + let DefiningTy::Closure(_, substs) = def_ty else { // Can't have BrEnv in functions, constants or generators. bug!("BrEnv outside of closure."); - } + }; + let hir::ExprKind::Closure(_, _, _, args_span, _) = + tcx.hir().expect_expr(self.mir_hir_id()).kind else { + bug!("Closure is not defined by a closure expr"); + }; + let region_name = self.synthesize_region_name(); + + let closure_kind_ty = substs.as_closure().kind_ty(); + let note = match closure_kind_ty.to_opt_closure_kind() { + Some(ty::ClosureKind::Fn) => { + "closure implements `Fn`, so references to captured variables \ + can't escape the closure" + } + Some(ty::ClosureKind::FnMut) => { + "closure implements `FnMut`, so references to captured variables \ + can't escape the closure" + } + Some(ty::ClosureKind::FnOnce) => { + bug!("BrEnv in a `FnOnce` closure"); + } + None => bug!("Closure kind not inferred in borrow check"), + }; + + Some(RegionName { + name: region_name, + source: RegionNameSource::SynthesizedFreeEnvRegion( + args_span, + note.to_string(), + ), + }) } ty::BoundRegionKind::BrAnon(_) => None, @@ -437,7 +433,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { span: Span, counter: usize, ) -> RegionNameHighlight { - let mut highlight = RegionHighlightMode::default(); + let mut highlight = RegionHighlightMode::new(self.infcx.tcx); highlight.highlighting_region_vid(needle_fr, counter); let type_name = self.infcx.extract_inference_diagnostics_data(ty.into(), Some(highlight)).name; @@ -504,7 +500,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } // Otherwise, let's descend into the referent types. - search_stack.push((referent_ty, &referent_hir_ty.ty)); + search_stack.push((*referent_ty, &referent_hir_ty.ty)); } // Match up something like `Foo<'1>` @@ -543,7 +539,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { (ty::Slice(elem_ty), hir::TyKind::Slice(elem_hir_ty)) | (ty::Array(elem_ty, _), hir::TyKind::Array(elem_hir_ty, _)) => { - search_stack.push((elem_ty, elem_hir_ty)); + search_stack.push((*elem_ty, elem_hir_ty)); } (ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => { @@ -704,7 +700,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::AsyncGeneratorKind::Block => " of async block", hir::AsyncGeneratorKind::Closure => " of async closure", hir::AsyncGeneratorKind::Fn => { - let parent_item = hir.get(hir.get_parent_item(mir_hir_id)); + let parent_item = hir.get_by_def_id(hir.get_parent_item(mir_hir_id)); let output = &parent_item .fn_decl() .expect("generator lowered from async fn should be in fn") @@ -765,45 +761,45 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { let hir = self.infcx.tcx.hir(); - if let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind { - let opaque_ty = hir.item(id); - if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { - bounds: - [ - hir::GenericBound::LangItemTrait( - hir::LangItem::Future, - _, - _, - hir::GenericArgs { - bindings: - [ - hir::TypeBinding { - ident: Ident { name: sym::Output, .. }, - kind: hir::TypeBindingKind::Equality { ty }, - .. - }, - ], - .. - }, - ), - ], - .. - }) = opaque_ty.kind - { - ty - } else { - span_bug!( - hir_ty.span, - "bounds from lowered return type of async fn did not match expected format: {:?}", - opaque_ty - ); - } - } else { + let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind else { span_bug!( hir_ty.span, "lowered return type of async fn is not OpaqueDef: {:?}", hir_ty ); + }; + let opaque_ty = hir.item(id); + if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { + bounds: + [ + hir::GenericBound::LangItemTrait( + hir::LangItem::Future, + _, + _, + hir::GenericArgs { + bindings: + [ + hir::TypeBinding { + ident: Ident { name: sym::Output, .. }, + kind: + hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) }, + .. + }, + ], + .. + }, + ), + ], + .. + }) = opaque_ty.kind + { + ty + } else { + span_bug!( + hir_ty.span, + "bounds from lowered return type of async fn did not match expected format: {:?}", + opaque_ty + ); } } @@ -822,7 +818,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { return None; } - let mut highlight = RegionHighlightMode::default(); + let mut highlight = RegionHighlightMode::new(tcx); highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); let type_name = self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name; diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index c03e4d8a44..73ced63e4d 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -5,12 +5,11 @@ use rustc_middle::mir::{BorrowKind, Mutability, Operand}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::mir::{Statement, StatementKind}; use rustc_middle::ty::TyCtxt; -use std::iter; use crate::{ borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, path_utils::*, AccessDepth, - Activation, ArtificialField, BorrowIndex, Deep, JustWrite, LocalMutationIsAllowed, MutateMode, - Read, ReadKind, ReadOrWrite, Reservation, Shallow, Write, WriteAndRead, WriteKind, + Activation, ArtificialField, BorrowIndex, Deep, LocalMutationIsAllowed, Read, ReadKind, + ReadOrWrite, Reservation, Shallow, Write, WriteKind, }; pub(super) fn generate_invalidates<'tcx>( @@ -59,37 +58,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { StatementKind::Assign(box (lhs, rhs)) => { self.consume_rvalue(location, rhs); - self.mutate_place(location, *lhs, Shallow(None), JustWrite); + self.mutate_place(location, *lhs, Shallow(None)); } StatementKind::FakeRead(box (_, _)) => { // Only relevant for initialized/liveness/safety checks. } StatementKind::SetDiscriminant { place, variant_index: _ } => { - self.mutate_place(location, **place, Shallow(None), JustWrite); - } - StatementKind::LlvmInlineAsm(asm) => { - for (o, output) in iter::zip(&asm.asm.outputs, &*asm.outputs) { - if o.is_indirect { - // FIXME(eddyb) indirect inline asm outputs should - // be encoded through MIR place derefs instead. - self.access_place( - location, - *output, - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - ); - } else { - self.mutate_place( - location, - *output, - if o.is_rw { Deep } else { Shallow(None) }, - if o.is_rw { WriteAndRead } else { JustWrite }, - ); - } - } - for (_, input) in asm.inputs.iter() { - self.consume_operand(location, input); - } + self.mutate_place(location, **place, Shallow(None)); } StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { ref src, @@ -142,7 +117,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { target: _, unwind: _, } => { - self.mutate_place(location, *drop_place, Deep, JustWrite); + self.mutate_place(location, *drop_place, Deep); self.consume_operand(location, new_value); } TerminatorKind::Call { @@ -158,7 +133,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.consume_operand(location, arg); } if let Some((dest, _ /*bb*/)) = destination { - self.mutate_place(location, *dest, Deep, JustWrite); + self.mutate_place(location, *dest, Deep); } } TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { @@ -181,7 +156,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } } - self.mutate_place(location, *resume_arg, Deep, JustWrite); + self.mutate_place(location, *resume_arg, Deep); } TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { // Invalidate all borrows of local places @@ -208,13 +183,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } InlineAsmOperand::Out { reg: _, late: _, place, .. } => { if let Some(place) = place { - self.mutate_place(location, place, Shallow(None), JustWrite); + self.mutate_place(location, place, Shallow(None)); } } InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { self.consume_operand(location, in_value); if let Some(out_place) = out_place { - self.mutate_place(location, out_place, Shallow(None), JustWrite); + self.mutate_place(location, out_place, Shallow(None)); } } InlineAsmOperand::Const { value: _ } @@ -238,13 +213,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { /// Simulates mutation of a place. - fn mutate_place( - &mut self, - location: Location, - place: Place<'tcx>, - kind: AccessDepth, - _mode: MutateMode, - ) { + fn mutate_place(&mut self, location: Location, place: Place<'tcx>, kind: AccessDepth) { self.access_place( location, place, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index fe34d6e7ca..04d914cdc7 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -9,6 +9,7 @@ #![feature(trusted_step)] #![feature(try_blocks)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_middle; @@ -40,7 +41,6 @@ use either::Either; use smallvec::SmallVec; use std::cell::RefCell; use std::collections::BTreeMap; -use std::iter; use std::mem; use std::rc::Rc; @@ -55,7 +55,6 @@ use rustc_mir_dataflow::MoveDataParamEnv; use self::diagnostics::{AccessKind, RegionName}; use self::location::LocationTable; use self::prefixes::PrefixSet; -use self::MutateMode::{JustWrite, WriteAndRead}; use facts::AllFacts; use self::path_utils::*; @@ -176,17 +175,20 @@ fn do_mir_borrowck<'a, 'tcx>( } } + let mut errors = error::BorrowckErrors::new(); + // Gather the upvars of a closure, if any. let tables = tcx.typeck_opt_const_arg(def); if let Some(ErrorReported) = tables.tainted_by_errors { infcx.set_tainted_by_errors(); + errors.set_tainted_by_errors(); } let upvars: Vec<_> = tables .closure_min_captures_flattened(def.did.to_def_id()) .map(|captured_place| { let capture = captured_place.info.capture_kind; let by_ref = match capture { - ty::UpvarCapture::ByValue(_) => false, + ty::UpvarCapture::ByValue => false, ty::UpvarCapture::ByRef(..) => true, }; Upvar { place: captured_place.clone(), by_ref } @@ -206,7 +208,6 @@ fn do_mir_borrowck<'a, 'tcx>( let location_table_owned = LocationTable::new(body); let location_table = &location_table_owned; - let mut errors_buffer = Vec::new(); let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) = match MoveData::gather_moves(&body, tcx, param_env) { Ok(move_data) => (move_data, Vec::new()), @@ -264,7 +265,7 @@ fn do_mir_borrowck<'a, 'tcx>( ®ioncx, &opt_closure_req, &opaque_type_values, - &mut errors_buffer, + &mut errors, ); // The various `flow_*` structures can be large. We drop `flow_inits` here @@ -311,9 +312,7 @@ fn do_mir_borrowck<'a, 'tcx>( access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), reservation_warnings: Default::default(), - move_error_reported: BTreeMap::new(), uninitialized_error_reported: Default::default(), - errors_buffer, regioncx: regioncx.clone(), used_mut: Default::default(), used_mut_upvars: SmallVec::new(), @@ -324,9 +323,10 @@ fn do_mir_borrowck<'a, 'tcx>( region_names: RefCell::default(), next_region_name: RefCell::new(1), polonius_output: None, + errors, }; promoted_mbcx.report_move_errors(move_errors); - errors_buffer = promoted_mbcx.errors_buffer; + errors = promoted_mbcx.errors; }; } @@ -344,9 +344,7 @@ fn do_mir_borrowck<'a, 'tcx>( access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), reservation_warnings: Default::default(), - move_error_reported: BTreeMap::new(), uninitialized_error_reported: Default::default(), - errors_buffer, regioncx: Rc::clone(®ioncx), used_mut: Default::default(), used_mut_upvars: SmallVec::new(), @@ -357,6 +355,7 @@ fn do_mir_borrowck<'a, 'tcx>( region_names: RefCell::default(), next_region_name: RefCell::new(1), polonius_output, + errors, }; // Compute and report region errors, if any. @@ -399,7 +398,7 @@ fn do_mir_borrowck<'a, 'tcx>( diag.message = initial_diag.styled_message().clone(); diag.span = initial_diag.span.clone(); - diag.buffer(&mut mbcx.errors_buffer); + mbcx.buffer_non_error_diag(diag); }, ); initial_diag.cancel(); @@ -424,7 +423,7 @@ fn do_mir_borrowck<'a, 'tcx>( mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals); debug!("mbcx.used_mut: {:?}", mbcx.used_mut); - let used_mut = mbcx.used_mut; + let used_mut = std::mem::take(&mut mbcx.used_mut); for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) { let local_decl = &mbcx.body.local_decls[local]; let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data { @@ -461,23 +460,13 @@ fn do_mir_borrowck<'a, 'tcx>( }) } - // Buffer any move errors that we collected and de-duplicated. - for (_, (_, diag)) in mbcx.move_error_reported { - diag.buffer(&mut mbcx.errors_buffer); - } - - if !mbcx.errors_buffer.is_empty() { - mbcx.errors_buffer.sort_by_key(|diag| diag.sort_span); - - for diag in mbcx.errors_buffer.drain(..) { - mbcx.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag); - } - } + let tainted_by_errors = mbcx.emit_errors(); let result = BorrowCheckResult { concrete_opaque_types: opaque_type_values, closure_requirements: opt_closure_req, used_mut_upvars: mbcx.used_mut_upvars, + tainted_by_errors, }; let body_with_facts = if return_body_with_facts { @@ -554,26 +543,9 @@ struct MirBorrowckCtxt<'cx, 'tcx> { /// for the activation of the borrow. reservation_warnings: FxHashMap, Span, Location, BorrowKind, BorrowData<'tcx>)>, - /// This field keeps track of move errors that are to be reported for given move indices. - /// - /// There are situations where many errors can be reported for a single move out (see #53807) - /// and we want only the best of those errors. - /// - /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the - /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the - /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once - /// all move errors have been reported, any diagnostics in this map are added to the buffer - /// to be emitted. - /// - /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary - /// when errors in the map are being re-added to the error buffer so that errors with the - /// same primary span come out in a consistent order. - move_error_reported: BTreeMap, (PlaceRef<'tcx>, DiagnosticBuilder<'cx>)>, /// This field keeps track of errors reported in the checking of uninitialized variables, /// so that we don't report seemingly duplicate errors. uninitialized_error_reported: FxHashSet>, - /// Errors to be reported buffer - errors_buffer: Vec, /// This field keeps track of all the local variables that are declared mut and are mutated. /// Used for the warning issued by an unused mutable local variable. used_mut: FxHashSet, @@ -605,6 +577,8 @@ struct MirBorrowckCtxt<'cx, 'tcx> { /// Results of Polonius analysis. polonius_output: Option>, + + errors: error::BorrowckErrors<'tcx>, } // Check that: @@ -630,7 +604,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx StatementKind::Assign(box (lhs, ref rhs)) => { self.consume_rvalue(location, (rhs, span), flow_state); - self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state); + self.mutate_place(location, (*lhs, span), Shallow(None), flow_state); } StatementKind::FakeRead(box (_, ref place)) => { // Read for match doesn't access any memory and is used to @@ -651,41 +625,8 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx ); } StatementKind::SetDiscriminant { place, variant_index: _ } => { - self.mutate_place(location, (**place, span), Shallow(None), JustWrite, flow_state); + self.mutate_place(location, (**place, span), Shallow(None), flow_state); } - StatementKind::LlvmInlineAsm(ref asm) => { - for (o, output) in iter::zip(&asm.asm.outputs, &*asm.outputs) { - if o.is_indirect { - // FIXME(eddyb) indirect inline asm outputs should - // be encoded through MIR place derefs instead. - self.access_place( - location, - (*output, o.span), - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state, - ); - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Use, - (output.as_ref(), o.span), - flow_state, - ); - } else { - self.mutate_place( - location, - (*output, o.span), - if o.is_rw { Deep } else { Shallow(None) }, - if o.is_rw { WriteAndRead } else { JustWrite }, - flow_state, - ); - } - } - for (_, input) in asm.inputs.iter() { - self.consume_operand(location, (input, span), flow_state); - } - } - StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { .. }) => { @@ -750,7 +691,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx target: _, unwind: _, } => { - self.mutate_place(loc, (drop_place, span), Deep, JustWrite, flow_state); + self.mutate_place(loc, (drop_place, span), Deep, flow_state); self.consume_operand(loc, (new_value, span), flow_state); } TerminatorKind::Call { @@ -766,7 +707,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx self.consume_operand(loc, (arg, span), flow_state); } if let Some((dest, _ /*bb*/)) = *destination { - self.mutate_place(loc, (dest, span), Deep, JustWrite, flow_state); + self.mutate_place(loc, (dest, span), Deep, flow_state); } } TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { @@ -780,7 +721,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx TerminatorKind::Yield { ref value, resume: _, resume_arg, drop: _ } => { self.consume_operand(loc, (value, span), flow_state); - self.mutate_place(loc, (resume_arg, span), Deep, JustWrite, flow_state); + self.mutate_place(loc, (resume_arg, span), Deep, flow_state); } TerminatorKind::InlineAsm { @@ -798,13 +739,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx } InlineAsmOperand::Out { reg: _, late: _, place, .. } => { if let Some(place) = place { - self.mutate_place( - loc, - (place, span), - Shallow(None), - JustWrite, - flow_state, - ); + self.mutate_place(loc, (place, span), Shallow(None), flow_state); } } InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { @@ -814,7 +749,6 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx loc, (out_place, span), Shallow(None), - JustWrite, flow_state, ); } @@ -886,12 +820,6 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx } } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum MutateMode { - JustWrite, - WriteAndRead, -} - use self::AccessDepth::{Deep, Shallow}; use self::ReadOrWrite::{Activation, Read, Reservation, Write}; @@ -977,7 +905,6 @@ enum LocalMutationIsAllowed { #[derive(Copy, Clone, Debug)] enum InitializationRequiringAction { - Update, Borrow, MatchOn, Use, @@ -994,7 +921,6 @@ struct RootPlace<'tcx> { impl InitializationRequiringAction { fn as_noun(self) -> &'static str { match self { - InitializationRequiringAction::Update => "update", InitializationRequiringAction::Borrow => "borrow", InitializationRequiringAction::MatchOn => "use", // no good noun InitializationRequiringAction::Use => "use", @@ -1005,7 +931,6 @@ impl InitializationRequiringAction { fn as_verb_in_past_tense(self) -> &'static str { match self { - InitializationRequiringAction::Update => "updated", InitializationRequiringAction::Borrow => "borrowed", InitializationRequiringAction::MatchOn => "matched on", InitializationRequiringAction::Use => "used", @@ -1077,7 +1002,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if conflict_error || mutability_error { debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind); - self.access_place_error_reported.insert((place_span.0, place_span.1)); } } @@ -1157,12 +1081,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { error_reported = true; match kind { ReadKind::Copy => { - this.report_use_while_mutably_borrowed(location, place_span, borrow) - .buffer(&mut this.errors_buffer); + let err = this + .report_use_while_mutably_borrowed(location, place_span, borrow); + this.buffer_error(err); } ReadKind::Borrow(bk) => { - this.report_conflicting_borrow(location, place_span, bk, borrow) - .buffer(&mut this.errors_buffer); + let err = + this.report_conflicting_borrow(location, place_span, bk, borrow); + this.buffer_error(err); } } Control::Break @@ -1212,8 +1138,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { error_reported = true; match kind { WriteKind::MutableBorrow(bk) => { - this.report_conflicting_borrow(location, place_span, bk, borrow) - .buffer(&mut this.errors_buffer); + let err = + this.report_conflicting_borrow(location, place_span, bk, borrow); + this.buffer_error(err); } WriteKind::StorageDeadOrDrop => this .report_borrowed_value_does_not_live_long_enough( @@ -1242,23 +1169,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { location: Location, place_span: (Place<'tcx>, Span), kind: AccessDepth, - mode: MutateMode, flow_state: &Flows<'cx, 'tcx>, ) { - // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. - match mode { - MutateMode::WriteAndRead => { - self.check_if_path_or_subpath_is_moved( - location, - InitializationRequiringAction::Update, - (place_span.0.as_ref(), place_span.1), - flow_state, - ); - } - MutateMode::JustWrite => { - self.check_if_assigned_path_is_moved(location, place_span, flow_state); - } - } + // Write of P[i] or *P requires P init'd. + self.check_if_assigned_path_is_moved(location, place_span, flow_state); // Special case: you can assign an immutable local variable // (e.g., `x = ...`) so long as it has never been initialized @@ -1490,9 +1404,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { bug!("temporary should be initialized exactly once") }; - let loc = match init.location { - InitLocation::Statement(stmt) => stmt, - _ => bug!("temporary initialized in arguments"), + let InitLocation::Statement(loc) = init.location else { + bug!("temporary initialized in arguments") }; let body = self.body; @@ -1634,7 +1547,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { yield_span, ); - err.buffer(&mut self.errors_buffer); + self.buffer_error(err); } } @@ -2110,10 +2023,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | WriteKind::MutableBorrow(BorrowKind::Shared) | WriteKind::MutableBorrow(BorrowKind::Shallow), ) => { - if let (Err(_), true) = ( - self.is_mutable(place.as_ref(), is_local_mutation_allowed), - self.errors_buffer.is_empty(), - ) { + if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err() + && !self.has_buffered_errors() + { // rust-lang/rust#46908: In pure NLL mode this code path should be // unreachable, but we use `delay_span_bug` because we can hit this when // dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug` @@ -2365,6 +2277,112 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } +mod error { + use super::*; + + pub struct BorrowckErrors<'tcx> { + /// This field keeps track of move errors that are to be reported for given move indices. + /// + /// There are situations where many errors can be reported for a single move out (see #53807) + /// and we want only the best of those errors. + /// + /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the + /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the + /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once + /// all move errors have been reported, any diagnostics in this map are added to the buffer + /// to be emitted. + /// + /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary + /// when errors in the map are being re-added to the error buffer so that errors with the + /// same primary span come out in a consistent order. + buffered_move_errors: + BTreeMap, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>)>, + /// Errors to be reported buffer + buffered: Vec, + /// Set to Some if we emit an error during borrowck + tainted_by_errors: Option, + } + + impl BorrowckErrors<'_> { + pub fn new() -> Self { + BorrowckErrors { + buffered_move_errors: BTreeMap::new(), + buffered: Default::default(), + tainted_by_errors: None, + } + } + + pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) { + self.tainted_by_errors = Some(ErrorReported {}); + t.buffer(&mut self.buffered); + } + + // For diagnostics we must not set `tainted_by_errors`. + pub fn buffer_non_error_diag(&mut self, t: DiagnosticBuilder<'_>) { + t.buffer(&mut self.buffered); + } + + pub fn set_tainted_by_errors(&mut self) { + self.tainted_by_errors = Some(ErrorReported {}); + } + } + + impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { + pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) { + self.errors.buffer_error(t); + } + + pub fn buffer_non_error_diag(&mut self, t: DiagnosticBuilder<'_>) { + self.errors.buffer_non_error_diag(t); + } + + pub fn buffer_move_error( + &mut self, + move_out_indices: Vec, + place_and_err: (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>), + ) -> bool { + if let Some((_, mut diag)) = + self.errors.buffered_move_errors.insert(move_out_indices, place_and_err) + { + // Cancel the old diagnostic so we don't ICE + diag.cancel(); + false + } else { + true + } + } + + pub fn emit_errors(&mut self) -> Option { + // Buffer any move errors that we collected and de-duplicated. + for (_, (_, diag)) in std::mem::take(&mut self.errors.buffered_move_errors) { + // We have already set tainted for this error, so just buffer it. + diag.buffer(&mut self.errors.buffered); + } + + if !self.errors.buffered.is_empty() { + self.errors.buffered.sort_by_key(|diag| diag.sort_span); + + for diag in self.errors.buffered.drain(..) { + self.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag); + } + } + + self.errors.tainted_by_errors + } + + pub fn has_buffered_errors(&self) -> bool { + self.errors.buffered.is_empty() + } + + pub fn has_move_error( + &self, + move_out_indices: &[MoveOutIndex], + ) -> Option<&(PlaceRef<'tcx>, DiagnosticBuilder<'cx>)> { + self.errors.buffered_move_errors.get(move_out_indices) + } + } +} + /// The degree of overlap between 2 places for borrow-checking. enum Overlap { /// The places might partially overlap - in this case, we give diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 6ffab16577..a2736fd115 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -1,7 +1,6 @@ //! The entry point of the NLL borrow checker. use rustc_data_structures::vec_map::VecMap; -use rustc_errors::Diagnostic; use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere}; @@ -9,7 +8,7 @@ use rustc_middle::mir::{ BasicBlock, Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted, }; -use rustc_middle::ty::{self, OpaqueTypeKey, RegionKind, RegionVid, Ty}; +use rustc_middle::ty::{self, OpaqueTypeKey, Region, RegionVid, Ty}; use rustc_span::symbol::sym; use std::env; use std::fmt::Debug; @@ -373,7 +372,7 @@ pub(super) fn dump_annotation<'a, 'tcx>( regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, opaque_type_values: &VecMap, Ty<'tcx>>, - errors_buffer: &mut Vec, + errors: &mut crate::error::BorrowckErrors<'tcx>, ) { let tcx = infcx.tcx; let base_def_id = tcx.typeck_root_def_id(body.source.def_id()); @@ -418,7 +417,7 @@ pub(super) fn dump_annotation<'a, 'tcx>( err.note(&format!("Inferred opaque type values:\n{:#?}", opaque_type_values)); } - err.buffer(errors_buffer); + errors.buffer_non_error_diag(err); } fn for_each_region_constraint( @@ -444,9 +443,9 @@ pub trait ToRegionVid { fn to_region_vid(self) -> RegionVid; } -impl<'tcx> ToRegionVid for &'tcx RegionKind { +impl<'tcx> ToRegionVid for Region<'tcx> { fn to_region_vid(self) -> RegionVid { - if let ty::ReVar(vid) = self { *vid } else { bug!("region is not an ReVar: {:?}", self) } + if let ty::ReVar(vid) = *self { vid } else { bug!("region is not an ReVar: {:?}", self) } } } diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index cfd3acb6bd..97233b930c 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -72,7 +72,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } let mut constraints: Vec<_> = self.constraints.outlives().iter().collect(); - constraints.sort(); + constraints.sort_by_key(|c| (c.sup, c.sub)); for constraint in &constraints { let OutlivesConstraint { sup, sub, locations, category, variance_info: _ } = constraint; let (name, arg) = match locations { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b39a28f79a..abd6a15334 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -612,7 +612,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn propagate_constraints(&mut self, _body: &Body<'tcx>) { debug!("constraints={:#?}", { let mut constraints: Vec<_> = self.constraints.outlives().iter().collect(); - constraints.sort(); + constraints.sort_by_key(|c| (c.sup, c.sub)); constraints .into_iter() .map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub))) @@ -1169,7 +1169,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { match verify_bound { VerifyBound::IfEq(test_ty, verify_bound1) => { - self.eval_if_eq(tcx, body, generic_ty, lower_bound, test_ty, verify_bound1) + self.eval_if_eq(tcx, body, generic_ty, lower_bound, *test_ty, verify_bound1) } VerifyBound::IsEmpty => { @@ -1178,7 +1178,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } VerifyBound::OutlivedBy(r) => { - let r_vid = self.to_region_vid(r); + let r_vid = self.to_region_vid(*r); self.eval_outlives(r_vid, lower_bound) } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 76b3be7976..60c48d8a76 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -133,7 +133,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { for vid in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) { match self.definitions[vid].external_name { None => {} - Some(&ty::ReStatic) => {} + Some(region) if region.is_static() => {} Some(region) => return region, } } @@ -183,7 +183,7 @@ fn check_opaque_type_parameter_valid( for (i, arg) in opaque_type_key.substs.iter().enumerate() { let arg_is_param = match arg.unpack() { GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), - GenericArgKind::Lifetime(ty::ReStatic) => { + GenericArgKind::Lifetime(lt) if lt.is_static() => { tcx.sess .struct_span_err(span, "non-defining opaque type use in defining scope") .span_label( @@ -196,9 +196,9 @@ fn check_opaque_type_parameter_valid( return false; } GenericArgKind::Lifetime(lt) => { - matches!(lt, ty::ReEarlyBound(_) | ty::ReFree(_)) + matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)) } - GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)), + GenericArgKind::Const(ct) => matches!(ct.val(), ty::ConstKind::Param(_)), }; if arg_is_param { diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index 4b6cab24cd..2876d60527 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -57,7 +57,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> { #[instrument(skip(self), level = "debug")] fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { - *ty = self.renumber_regions(ty); + *ty = self.renumber_regions(*ty); debug!(?ty); } @@ -72,12 +72,12 @@ impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> { #[instrument(skip(self), level = "debug")] fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) { let old_region = *region; - *region = self.renumber_regions(&old_region); + *region = self.renumber_regions(old_region); debug!(?region); } - fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _location: Location) { - *constant = self.renumber_regions(&*constant); + fn visit_const(&mut self, constant: &mut ty::Const<'tcx>, _location: Location) { + *constant = self.renumber_regions(*constant); } } diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index a3b39591f8..5022cb98b8 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -105,8 +105,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // create new region variables, which can't be done later when // verifying these bounds. if t1.has_placeholders() { - t1 = tcx.fold_regions(&t1, &mut false, |r, _| match *r { - ty::RegionKind::RePlaceholder(placeholder) => { + t1 = tcx.fold_regions(t1, &mut false, |r, _| match *r { + ty::RePlaceholder(placeholder) => { self.constraints.placeholder_region(self.infcx, placeholder) } _ => r, @@ -142,8 +142,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> ty::RegionVid { - if let ty::RePlaceholder(placeholder) = r { - self.constraints.placeholder_region(self.infcx, *placeholder).to_region_vid() + if let ty::RePlaceholder(placeholder) = *r { + self.constraints.placeholder_region(self.infcx, placeholder).to_region_vid() } else { self.universal_regions.to_region_vid(r) } diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index fec6bdf314..9a028147a4 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -358,7 +358,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // `where Type:` is lowered to `where Type: 'empty` so that // we check `Type` is well formed, but there's no use for // this bound here. - if let ty::ReEmpty(_) = r1 { + if r1.is_empty() { return; } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 094af20f52..169de23fac 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -171,7 +171,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { for (local, location) in drop_used { if !live_locals.contains(&local) { let local_ty = self.cx.body.local_decls[local].ty; - if local_ty.has_free_regions(self.cx.typeck.tcx()) { + if local_ty.has_free_regions() { self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations); } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 1f745f977d..a582409009 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -312,6 +312,7 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { } } +#[track_caller] fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: &str) { // We sometimes see MIR failures (notably predicate failures) due to // the fact that we check rvalue sized predicates here. So use `delay_span_bug` @@ -382,7 +383,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } else { let tcx = self.tcx(); let maybe_uneval = match constant.literal { - ConstantKind::Ty(ct) => match ct.val { + ConstantKind::Ty(ct) => match ct.val() { ty::ConstKind::Unevaluated(uv) => Some(uv), _ => None, }, @@ -425,7 +426,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( constant.literal.ty(), uv.def.did, - UserSubsts { substs: uv.substs(self.tcx()), user_self_ty: None }, + UserSubsts { substs: uv.substs, user_self_ty: None }, )), ) { span_mirbug!( @@ -481,7 +482,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { // then remove the outermost reference so we can check the // type annotation for the remaining type. if let ty::Ref(_, rty, _) = local_decl.ty.kind() { - rty + *rty } else { bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty); } @@ -715,7 +716,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { PlaceTy::from_ty(match base_ty.kind() { ty::Array(inner, _) => { assert!(!from_end, "array subslices should not use from_end"); - tcx.mk_array(inner, to - from) + tcx.mk_array(*inner, to - from) } ty::Slice(..) => { assert!(from_end, "slice subslices should use from_end"); @@ -757,6 +758,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }, ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(place, fty); + let fty = self.cx.normalize(fty, location); match self.field_ty(place, base, field, location) { Ok(ty) => { let ty = self.cx.normalize(ty, location); @@ -1477,7 +1479,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - | StatementKind::LlvmInlineAsm { .. } | StatementKind::Retag { .. } | StatementKind::Coverage(..) | StatementKind::Nop => {} @@ -1736,7 +1737,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::Boring }; if let Err(terr) = - self.sub_types(op_arg_ty, fn_arg, term_location.to_locations(), category) + self.sub_types(op_arg_ty, *fn_arg, term_location.to_locations(), category) { span_mirbug!( self, @@ -1955,7 +1956,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) { if let Operand::Constant(constant) = op { let maybe_uneval = match constant.literal { - ConstantKind::Ty(ct) => match ct.val { + ConstantKind::Ty(ct) => match ct.val() { ty::ConstKind::Unevaluated(uv) => Some(uv), _ => None, }, @@ -1969,7 +1970,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let predicates = self.prove_closure_bounds( tcx, def_id.expect_local(), - uv.substs(tcx), + uv.substs, location, ); self.normalize_and_prove_instantiated_predicates( @@ -2047,7 +2048,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - Rvalue::NullaryOp(_, ty) => { + &Rvalue::NullaryOp(_, ty) => { let trait_ref = ty::TraitRef { def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), substs: tcx.mk_substs_trait(ty, &[]), @@ -2065,7 +2066,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let trait_ref = ty::TraitRef { def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), - substs: tcx.mk_substs_trait(ty, &[]), + substs: tcx.mk_substs_trait(*ty, &[]), }; self.prove_trait_ref( @@ -2092,7 +2093,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig); if let Err(terr) = self.eq_types( - ty, + *ty, ty_fn_ptr_from, location.to_locations(), ConstraintCategory::Cast, @@ -2116,7 +2117,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety)); if let Err(terr) = self.eq_types( - ty, + *ty, ty_fn_ptr_from, location.to_locations(), ConstraintCategory::Cast, @@ -2145,7 +2146,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig); if let Err(terr) = self.eq_types( - ty, + *ty, ty_fn_ptr_from, location.to_locations(), ConstraintCategory::Cast, @@ -2208,8 +2209,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } }; if let Err(terr) = self.sub_types( - ty_from, - ty_to, + *ty_from, + *ty_to, location.to_locations(), ConstraintCategory::Cast, ) { @@ -2277,8 +2278,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } if let Err(terr) = self.sub_types( - ty_elem, - ty_to, + *ty_elem, + *ty_to, location.to_locations(), ConstraintCategory::Cast, ) { @@ -2296,7 +2297,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { CastKind::Misc => { let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); - let cast_ty_to = CastTy::from_ty(ty); + let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { (None, _) | (_, None | Some(CastTy::FnPtr)) @@ -2317,7 +2318,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } Rvalue::Ref(region, _borrow_kind, borrowed_place) => { - self.add_reborrow_constraint(&body, location, region, borrowed_place); + self.add_reborrow_constraint(&body, location, *region, borrowed_place); } Rvalue::BinaryOp( @@ -2529,9 +2530,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { body, ); let category = if let Some(field) = field { - 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) + ConstraintCategory::ClosureUpvar(field) } else { ConstraintCategory::Boring }; diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index cc3fe0a123..590a7a9164 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -117,7 +117,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> // We don't have to worry about the equality of consts during borrow checking // as consts always have a static lifetime. - fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {} + fn const_equate(&mut self, _a: Const<'tcx>, _b: Const<'tcx>) {} fn normalization() -> NormalizationStrategy { NormalizationStrategy::Eager diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index b986df403f..72de380546 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -180,8 +180,9 @@ pub enum RegionClassification { /// anywhere. There is only one, `'static`. Global, - /// An **external** region is only relevant for closures. In that - /// case, it refers to regions that are free in the closure type + /// An **external** region is only relevant for + /// closures, generators, and inline consts. In that + /// case, it refers to regions that are free in the type /// -- basically, something bound in the surrounding context. /// /// Consider this example: @@ -198,8 +199,8 @@ pub enum RegionClassification { /// Here, the lifetimes `'a` and `'b` would be **external** to the /// closure. /// - /// If we are not analyzing a closure, there are no external - /// lifetimes. + /// If we are not analyzing a closure/generator/inline-const, + /// there are no external lifetimes. External, /// A **local** lifetime is one about which we know the full set @@ -322,7 +323,7 @@ impl<'tcx> UniversalRegions<'tcx> { /// See `UniversalRegionIndices::to_region_vid`. pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::ReEmpty(ty::UniverseIndex::ROOT) = r { + if let ty::ReEmpty(ty::UniverseIndex::ROOT) = *r { self.root_empty } else { self.indices.to_region_vid(r) @@ -424,22 +425,30 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.did.to_def_id()); - // If this is a closure or generator, then the late-bound regions from the enclosing - // function are actually external regions to us. For example, here, 'a is not local - // to the closure c (although it is local to the fn foo): - // fn foo<'a>() { - // let c = || { let x: &'a u32 = ...; } - // } - if self.mir_def.did.to_def_id() != typeck_root_def_id { + // If this is is a 'root' body (not a closure/generator/inline const), then + // there are no extern regions, so the local regions start at the same + // position as the (empty) sub-list of extern regions + let first_local_index = if self.mir_def.did.to_def_id() == typeck_root_def_id { + first_extern_index + } else { + // If this is a closure, generator, or inline-const, then the late-bound regions from the enclosing + // function are actually external regions to us. For example, here, 'a is not local + // to the closure c (although it is local to the fn foo): + // fn foo<'a>() { + // let c = || { let x: &'a u32 = ...; } + // } self.infcx - .replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices) - } - - let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty); + .replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices); + // Any regions created during the execution of `defining_ty` or during the above + // late-bound region replacement are all considered 'extern' regions + self.infcx.num_region_vars() + }; // "Liberate" the late-bound regions. These correspond to // "local" free regions. - let first_local_index = self.infcx.num_region_vars(); + + let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty); + let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars( FR, self.mir_def.did, @@ -503,7 +512,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { first_local_index, num_universals, defining_ty, - unnormalized_output_ty, + unnormalized_output_ty: *unnormalized_output_ty, unnormalized_input_tys, yield_ty, } @@ -796,7 +805,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> { /// during initialization. Relies on the `indices` map having been /// fully initialized. pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::ReVar(..) = r { + if let ty::ReVar(..) = *r { r.to_region_vid() } else { *self diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 1a93b9be99..ac37c4973d 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -16,13 +16,13 @@ use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; pub struct AsmArgs { - templates: Vec>, - operands: Vec<(ast::InlineAsmOperand, Span)>, + pub templates: Vec>, + pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxHashMap, reg_args: FxHashSet, - clobber_abis: Vec<(Symbol, Span)>, + pub clobber_abis: Vec<(Symbol, Span)>, options: ast::InlineAsmOptions, - options_spans: Vec, + pub options_spans: Vec, } fn parse_args<'a>( @@ -50,15 +50,6 @@ pub fn parse_asm_args<'a>( return Err(diag.struct_span_err(sp, "requires at least a template string argument")); } - // Detect use of the legacy llvm_asm! syntax (which used to be called asm!) - if !is_global_asm && p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) { - let mut err = - diag.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported"); - err.note("consider migrating to the new asm! syntax specified in RFC 2873"); - err.note("alternatively, switch to llvm_asm! to keep your code working as it is"); - return Err(err); - } - let first_template = p.parse_expr()?; let mut args = AsmArgs { templates: vec![first_template], @@ -709,11 +700,11 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option match args.named_args.get(&name) { + parse::ArgumentNamed(name, span) => match args.named_args.get(&name) { Some(&idx) => Some(idx), None => { let msg = format!("there is no argument named `{}`", name); - ecx.struct_span_err(span, &msg).emit(); + ecx.struct_span_err(template_span.from_inner(span), &msg).emit(); None } }, diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index eb08170959..c06af5206d 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -6,9 +6,7 @@ use rustc_expand::base::{self, DummyResult}; /// Emits errors for literal expressions that are invalid inside and outside of an array. fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P, is_nested: bool) { - let lit = if let ast::ExprKind::Lit(lit) = &expr.kind { - lit - } else { + let ast::ExprKind::Lit(lit) = &expr.kind else { unreachable!(); }; match lit.kind { diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 8c53094b62..1d1eee88a6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -222,9 +222,6 @@ fn validate_default_attribute( "this method must only be called with a variant that has a `#[default]` attribute", ), [first, rest @ ..] => { - // FIXME(jhpratt) Do we want to perform this check? It doesn't exist - // for `#[inline]`, `#[non_exhaustive]`, and presumably others. - let suggestion_text = if rest.len() == 1 { "try removing this" } else { "try removing these" }; diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 367a5aa732..0ca7988ca1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -134,7 +134,7 @@ fn inject_impl_of_structural_trait( // Create the type of `self`. // - // in addition, remove defaults from type params (impls cannot have them). + // in addition, remove defaults from generic params (impls cannot have them). let self_params: Vec<_> = generics .params .iter_mut() diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 154c51b2c7..6141d00f69 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -4,13 +4,15 @@ use Position::*; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; +use rustc_ast::visit::{self, Visitor}; use rustc_ast::{token, BlockCheckMode, UnsafeSource}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; use rustc_expand::base::{self, *}; use rustc_parse_format as parse; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{MultiSpan, Span}; +use rustc_span::{InnerSpan, MultiSpan, Span}; +use smallvec::SmallVec; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -24,7 +26,7 @@ enum ArgumentType { enum Position { Exact(usize), Capture(usize), - Named(Symbol), + Named(Symbol, InnerSpan), } struct Context<'a, 'b> { @@ -245,13 +247,13 @@ impl<'a, 'b> Context<'a, 'b> { match *p { parse::String(_) => {} parse::NextArgument(ref mut arg) => { - if let parse::ArgumentNamed(s) = arg.position { + if let parse::ArgumentNamed(s, _) = arg.position { arg.position = parse::ArgumentIs(lookup(s)); } - if let parse::CountIsName(s) = arg.format.width { + if let parse::CountIsName(s, _) = arg.format.width { arg.format.width = parse::CountIsParam(lookup(s)); } - if let parse::CountIsName(s) = arg.format.precision { + if let parse::CountIsName(s, _) = arg.format.precision { arg.format.precision = parse::CountIsParam(lookup(s)); } } @@ -274,7 +276,7 @@ impl<'a, 'b> Context<'a, 'b> { // it's written second, so it should come after width/precision. let pos = match arg.position { parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), - parse::ArgumentNamed(s) => Named(s), + parse::ArgumentNamed(s, span) => Named(s, span), }; let ty = Placeholder(match arg.format.ty { @@ -344,8 +346,8 @@ impl<'a, 'b> Context<'a, 'b> { parse::CountIsParam(i) => { self.verify_arg_type(Exact(i), Count); } - parse::CountIsName(s) => { - self.verify_arg_type(Named(s), Count); + parse::CountIsName(s, span) => { + self.verify_arg_type(Named(s, span), Count); } } } @@ -531,7 +533,7 @@ impl<'a, 'b> Context<'a, 'b> { } } - Named(name) => { + Named(name, span) => { match self.names.get(&name) { Some(&idx) => { // Treat as positional arg. @@ -546,7 +548,7 @@ impl<'a, 'b> Context<'a, 'b> { self.arg_types.push(Vec::new()); self.arg_unique_types.push(Vec::new()); let span = if self.is_literal { - *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp) + self.fmtsp.from_inner(span) } else { self.fmtsp }; @@ -557,7 +559,7 @@ impl<'a, 'b> Context<'a, 'b> { } else { let msg = format!("there is no argument named `{}`", name); let sp = if self.is_literal { - *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp) + self.fmtsp.from_inner(span) } else { self.fmtsp }; @@ -627,7 +629,7 @@ impl<'a, 'b> Context<'a, 'b> { } parse::CountImplied => count(sym::Implied, None), // should never be the case, names are already resolved - parse::CountIsName(_) => panic!("should never happen"), + parse::CountIsName(..) => panic!("should never happen"), } } @@ -674,7 +676,7 @@ impl<'a, 'b> Context<'a, 'b> { // should never be the case, because names are already // resolved. - parse::ArgumentNamed(_) => panic!("should never happen"), + parse::ArgumentNamed(..) => panic!("should never happen"), } }; @@ -756,78 +758,109 @@ impl<'a, 'b> Context<'a, 'b> { /// Actually builds the expression which the format_args! block will be /// expanded to. fn into_expr(self) -> P { - let mut args = Vec::with_capacity( + let mut original_args = self.args; + let mut fmt_args = Vec::with_capacity( self.arg_unique_types.iter().map(|v| v.len()).sum::() + self.count_args.len(), ); - let mut heads = Vec::with_capacity(self.args.len()); // First, build up the static array which will become our precompiled // format "string" let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces); - // Before consuming the expressions, we have to remember spans for - // count arguments as they are now generated separate from other - // arguments, hence have no access to the `P`'s. - let spans_pos: Vec<_> = self.args.iter().map(|e| e.span).collect(); - - // Right now there is a bug such that for the expression: - // foo(bar(&1)) - // the lifetime of `1` doesn't outlast the call to `bar`, so it's not - // valid for the call to `foo`. To work around this all arguments to the - // format! string are shoved into locals. Furthermore, we shove the address - // of each variable because we don't want to move out of the arguments - // passed to this function. - for (i, e) in self.args.into_iter().enumerate() { - for arg_ty in self.arg_unique_types[i].iter() { - args.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, i)); - } - // use the arg span for `&arg` so that borrowck errors - // point to the specific expression passed to the macro - // (the span is otherwise unavailable in MIR) - heads.push(self.ecx.expr_addr_of(e.span.with_ctxt(self.macsp.ctxt()), e)); - } - for index in self.count_args { - let span = spans_pos[index]; - args.push(Context::format_arg(self.ecx, self.macsp, span, &Count, index)); + // We need to construct a &[ArgumentV1] to pass into the fmt::Arguments + // constructor. In general the expressions in this slice might be + // permuted from their order in original_args (such as in the case of + // "{1} {0}"), or may have multiple entries referring to the same + // element of original_args ("{0} {0}"). + // + // The following vector has one item per element of our output slice, + // identifying the index of which element of original_args it's passing, + // and that argument's type. + let mut fmt_arg_index_and_ty = SmallVec::<[(usize, &ArgumentType); 8]>::new(); + for (i, unique_types) in self.arg_unique_types.iter().enumerate() { + fmt_arg_index_and_ty.extend(unique_types.iter().map(|ty| (i, ty))); } + fmt_arg_index_and_ty.extend(self.count_args.iter().map(|&i| (i, &Count))); - let args_array = self.ecx.expr_vec(self.macsp, args); - - // Constructs an AST equivalent to: + // Figure out whether there are permuted or repeated elements. If not, + // we can generate simpler code. // - // match (&arg0, &arg1) { - // (tmp0, tmp1) => args_array - // } + // The sequence has no indices out of order or repeated if: for every + // adjacent pair of elements, the first one's index is less than the + // second one's index. + let nicely_ordered = + fmt_arg_index_and_ty.array_windows().all(|[(i, _i_ty), (j, _j_ty)]| i < j); + + // We want to emit: // - // It was: + // [ArgumentV1::new(&$arg0, …), ArgumentV1::new(&$arg1, …), …] // - // let tmp0 = &arg0; - // let tmp1 = &arg1; - // args_array + // However, it's only legal to do so if $arg0, $arg1, … were written in + // exactly that order by the programmer. When arguments are permuted, we + // want them evaluated in the order written by the programmer, not in + // the order provided to fmt::Arguments. When arguments are repeated, we + // want the expression evaluated only once. // - // Because of #11585 the new temporary lifetime rule, the enclosing - // statements for these temporaries become the let's themselves. - // If one or more of them are RefCell's, RefCell borrow() will also - // end there; they don't last long enough for args_array to use them. - // The match expression solves the scope problem. + // Further, if any arg _after the first one_ contains a yield point such + // as `await` or `yield`, the above short form is inconvenient for the + // caller because it would keep a temporary of type ArgumentV1 alive + // across the yield point. ArgumentV1 can't implement Send since it + // holds a type-erased arbitrary type. // - // Note, it may also very well be transformed to: + // Thus in the not nicely ordered case, and in the yielding case, we + // emit the following instead: // - // match arg0 { - // ref tmp0 => { - // match arg1 => { - // ref tmp1 => args_array } } } + // match (&$arg0, &$arg1, …) { + // args => [ArgumentV1::new(args.$i, …), ArgumentV1::new(args.$j, …), …] + // } // - // But the nested match expression is proved to perform not as well - // as series of let's; the first approach does. - let args_match = { - let pat = self.ecx.pat_ident(self.macsp, Ident::new(sym::_args, self.macsp)); - let arm = self.ecx.arm(self.macsp, pat, args_array); - let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads)); - self.ecx.expr_match(self.macsp, head, vec![arm]) - }; + // for the sequence of indices $i, $j, … governed by fmt_arg_index_and_ty. + // This more verbose representation ensures that all arguments are + // evaluated a single time each, in the order written by the programmer, + // and that the surrounding future/generator (if any) is Send whenever + // possible. + let no_need_for_match = + nicely_ordered && !original_args.iter().skip(1).any(|e| may_contain_yield_point(e)); + + for (arg_index, arg_ty) in fmt_arg_index_and_ty { + let e = &mut original_args[arg_index]; + let span = e.span; + let arg = if no_need_for_match { + let expansion_span = e.span.with_ctxt(self.macsp.ctxt()); + // The indices are strictly ordered so e has not been taken yet. + self.ecx.expr_addr_of(expansion_span, P(e.take())) + } else { + let def_site = self.ecx.with_def_site_ctxt(span); + let args_tuple = self.ecx.expr_ident(def_site, Ident::new(sym::args, def_site)); + let member = Ident::new(sym::integer(arg_index), def_site); + self.ecx.expr(def_site, ast::ExprKind::Field(args_tuple, member)) + }; + fmt_args.push(Context::format_arg(self.ecx, self.macsp, span, arg_ty, arg)); + } - let args_slice = self.ecx.expr_addr_of(self.macsp, args_match); + let args_array = self.ecx.expr_vec(self.macsp, fmt_args); + let args_slice = self.ecx.expr_addr_of( + self.macsp, + if no_need_for_match { + args_array + } else { + // In the !no_need_for_match case, none of the exprs were moved + // away in the previous loop. + // + // This uses the arg span for `&arg` so that borrowck errors + // point to the specific expression passed to the macro (the + // span is otherwise unavailable in the MIR used by borrowck). + let heads = original_args + .into_iter() + .map(|e| self.ecx.expr_addr_of(e.span.with_ctxt(self.macsp.ctxt()), e)) + .collect(); + + let pat = self.ecx.pat_ident(self.macsp, Ident::new(sym::args, self.macsp)); + let arm = self.ecx.arm(self.macsp, pat, args_array); + let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads)); + self.ecx.expr_match(self.macsp, head, vec![arm]) + }, + ); // Now create the fmt::Arguments struct with all our locals we created. let (fn_name, fn_args) = if self.all_pieces_simple { @@ -860,11 +893,9 @@ impl<'a, 'b> Context<'a, 'b> { macsp: Span, mut sp: Span, ty: &ArgumentType, - arg_index: usize, + arg: P, ) -> P { sp = ecx.with_def_site_ctxt(sp); - let arg = ecx.expr_ident(sp, Ident::new(sym::_args, sp)); - let arg = ecx.expr(sp, ast::ExprKind::Field(arg, Ident::new(sym::integer(arg_index), sp))); let trait_ = match *ty { Placeholder(trait_) if trait_ == "" => return DummyResult::raw_expr(sp, true), Placeholder(trait_) => trait_, @@ -873,11 +904,21 @@ impl<'a, 'b> Context<'a, 'b> { return ecx.expr_call_global(macsp, path, vec![arg]); } }; + let new_fn_name = match trait_ { + "Display" => "new_display", + "Debug" => "new_debug", + "LowerExp" => "new_lower_exp", + "UpperExp" => "new_upper_exp", + "Octal" => "new_octal", + "Pointer" => "new_pointer", + "Binary" => "new_binary", + "LowerHex" => "new_lower_hex", + "UpperHex" => "new_upper_hex", + _ => unreachable!(), + }; - let path = ecx.std_path(&[sym::fmt, Symbol::intern(trait_), sym::fmt]); - let format_fn = ecx.path_global(sp, path); - let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, sym::new]); - ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)]) + let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, Symbol::intern(new_fn_name)]); + ecx.expr_call_global(sp, path, vec![arg]) } } @@ -1213,3 +1254,35 @@ pub fn expand_preparsed_format_args( cx.into_expr() } + +fn may_contain_yield_point(e: &ast::Expr) -> bool { + struct MayContainYieldPoint(bool); + + impl Visitor<'_> for MayContainYieldPoint { + fn visit_expr(&mut self, e: &ast::Expr) { + if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind { + self.0 = true; + } else { + visit::walk_expr(self, e); + } + } + + fn visit_mac_call(&mut self, _: &ast::MacCall) { + self.0 = true; + } + + fn visit_attribute(&mut self, _: &ast::Attribute) { + // Conservatively assume this may be a proc macro attribute in + // expression position. + self.0 = true; + } + + fn visit_item(&mut self, _: &ast::Item) { + // Do not recurse into nested items. + } + } + + let mut visitor = MayContainYieldPoint(false); + visitor.visit_expr(e); + visitor.0 +} diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index a433876147..36cfbba45d 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -26,14 +26,14 @@ pub fn expand( // Allow using `#[global_allocator]` on an item statement // FIXME - if we get deref patterns, use them to reduce duplication here - let (item, is_stmt) = match &item { + let (item, is_stmt, ty_span) = match &item { Annotatable::Item(item) => match item.kind { - ItemKind::Static(..) => (item, false), + ItemKind::Static(ref ty, ..) => (item, false, ecx.with_def_site_ctxt(ty.span)), _ => return not_static(), }, Annotatable::Stmt(stmt) => match &stmt.kind { StmtKind::Item(item_) => match item_.kind { - ItemKind::Static(..) => (item_, true), + ItemKind::Static(ref ty, ..) => (item_, true, ecx.with_def_site_ctxt(ty.span)), _ => return not_static(), }, _ => return not_static(), @@ -43,13 +43,14 @@ pub fn expand( // Generate a bunch of new items using the AllocFnFactory let span = ecx.with_def_site_ctxt(item.span); - let f = AllocFnFactory { span, kind: AllocatorKind::Global, global: item.ident, cx: ecx }; + let f = + AllocFnFactory { span, ty_span, kind: AllocatorKind::Global, global: item.ident, cx: ecx }; // Generate item statements for the allocator methods. let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect(); // Generate anonymous constant serving as container for the allocator methods. - let const_ty = ecx.ty(span, TyKind::Tup(Vec::new())); + let const_ty = ecx.ty(ty_span, TyKind::Tup(Vec::new())); let const_body = ecx.expr_block(ecx.block(span, stmts)); let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body); let const_item = if is_stmt { @@ -64,6 +65,7 @@ pub fn expand( struct AllocFnFactory<'a, 'b> { span: Span, + ty_span: Span, kind: AllocatorKind, global: Ident, cx: &'b ExtCtxt<'a>, @@ -97,18 +99,18 @@ impl AllocFnFactory<'_, '_> { self.attrs(), kind, ); - self.cx.stmt_item(self.span, item) + self.cx.stmt_item(self.ty_span, item) } fn call_allocator(&self, method: Symbol, mut args: Vec>) -> P { let method = self.cx.std_path(&[sym::alloc, sym::GlobalAlloc, method]); - let method = self.cx.expr_path(self.cx.path(self.span, method)); - let allocator = self.cx.path_ident(self.span, self.global); + let method = self.cx.expr_path(self.cx.path(self.ty_span, method)); + let allocator = self.cx.path_ident(self.ty_span, self.global); let allocator = self.cx.expr_path(allocator); - let allocator = self.cx.expr_addr_of(self.span, allocator); + let allocator = self.cx.expr_addr_of(self.ty_span, allocator); args.insert(0, allocator); - self.cx.expr_call(self.span, method, args) + self.cx.expr_call(self.ty_span, method, args) } fn attrs(&self) -> Vec { diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index e43cece4f1..3887739994 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -2,14 +2,18 @@ //! injecting code into the crate before it is lowered to HIR. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(array_windows)] #![feature(box_patterns)] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(is_sorted)] #![feature(nll)] +#![feature(let_else)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] extern crate proc_macro; @@ -34,7 +38,6 @@ mod env; mod format; mod format_foreign; mod global_allocator; -mod llvm_asm; mod log_syntax; mod source_util; mod test; @@ -78,7 +81,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { include_str: source_util::expand_include_str, include: source_util::expand_include, line: source_util::expand_line, - llvm_asm: llvm_asm::expand_llvm_asm, log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, option_env: env::expand_option_env, diff --git a/compiler/rustc_builtin_macros/src/llvm_asm.rs b/compiler/rustc_builtin_macros/src/llvm_asm.rs deleted file mode 100644 index d72bfa660e..0000000000 --- a/compiler/rustc_builtin_macros/src/llvm_asm.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Llvm-style inline assembly support. -// -use State::*; - -use rustc_ast as ast; -use rustc_ast::ptr::P; -use rustc_ast::token::{self, Token}; -use rustc_ast::tokenstream::{self, TokenStream}; -use rustc_ast::LlvmAsmDialect; -use rustc_errors::{struct_span_err, DiagnosticBuilder, PResult}; -use rustc_expand::base::*; -use rustc_parse::parser::Parser; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; - -enum State { - Asm, - Outputs, - Inputs, - Clobbers, - Options, - StateNone, -} - -impl State { - fn next(&self) -> State { - match *self { - Asm => Outputs, - Outputs => Inputs, - Inputs => Clobbers, - Clobbers => Options, - Options => StateNone, - StateNone => StateNone, - } - } -} - -const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel]; - -pub fn expand_llvm_asm<'cx>( - cx: &'cx mut ExtCtxt<'_>, - sp: Span, - tts: TokenStream, -) -> Box { - let mut inline_asm = match parse_inline_asm(cx, sp, tts) { - Ok(Some(inline_asm)) => inline_asm, - Ok(None) => return DummyResult::any(sp), - Err(mut err) => { - err.emit(); - return DummyResult::any(sp); - } - }; - - // If there are no outputs, the inline assembly is executed just for its side effects, - // so ensure that it is volatile - if inline_asm.outputs.is_empty() { - inline_asm.volatile = true; - } - - MacEager::expr(P(ast::Expr { - id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::LlvmInlineAsm(P(inline_asm)), - span: cx.with_def_site_ctxt(sp), - attrs: ast::AttrVec::new(), - tokens: None, - })) -} - -fn parse_asm_str<'a>(p: &mut Parser<'a>) -> PResult<'a, Symbol> { - match p.parse_str_lit() { - Ok(str_lit) => Ok(str_lit.symbol_unescaped), - Err(opt_lit) => { - let span = opt_lit.map_or(p.token.span, |lit| lit.span); - let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); - err.span_label(span, "not a string literal"); - Err(err) - } - } -} - -fn parse_inline_asm<'a>( - cx: &mut ExtCtxt<'a>, - sp: Span, - tts: TokenStream, -) -> Result, DiagnosticBuilder<'a>> { - // Split the tts before the first colon, to avoid `llvm_asm!("x": y)` being - // parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription. - let first_colon = tts - .trees() - .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::Empty; - let mut asm_str_style = None; - let mut outputs = Vec::new(); - let mut inputs = Vec::new(); - let mut clobs = Vec::new(); - let mut volatile = false; - let mut alignstack = false; - let mut dialect = LlvmAsmDialect::Att; - - let mut state = Asm; - - 'statement: loop { - match state { - Asm => { - if asm_str_style.is_some() { - // If we already have a string with instructions, - // ending up in Asm state again is an error. - return Err(struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - sp, - E0660, - "malformed inline assembly" - )); - } - // Nested parser, stop before the first colon (see above). - let mut p2 = cx.new_parser_from_tts(tts.trees().take(first_colon).collect()); - - if p2.token == token::Eof { - let mut err = - cx.struct_span_err(sp, "macro requires a string literal as an argument"); - err.span_label(sp, "string literal required"); - return Err(err); - } - - let expr = p2.parse_expr()?; - let (s, style) = - match expr_to_string(cx, expr, "inline assembly must be a string literal") { - Some((s, st)) => (s, st), - None => return Ok(None), - }; - - // This is most likely malformed. - if p2.token != token::Eof { - let mut extra_tts = p2.parse_all_token_trees()?; - extra_tts.extend(tts.trees().skip(first_colon)); - p = cx.new_parser_from_tts(extra_tts.into_iter().collect()); - } - - asm = s; - asm_str_style = Some(style); - } - Outputs => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !outputs.is_empty() { - p.eat(&token::Comma); - } - - let constraint = parse_asm_str(&mut p)?; - - let span = p.prev_token.span; - - p.expect(&token::OpenDelim(token::Paren))?; - let expr = p.parse_expr()?; - p.expect(&token::CloseDelim(token::Paren))?; - - // Expands a read+write operand into two operands. - // - // Use '+' modifier when you want the same expression - // to be both an input and an output at the same time. - // It's the opposite of '=&' which means that the memory - // cannot be shared with any other operand (usually when - // a register is clobbered early.) - let constraint_str = constraint.as_str(); - let mut ch = constraint_str.chars(); - let output = match ch.next() { - Some('=') => None, - Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))), - _ => { - struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - span, - E0661, - "output operand constraint lacks '=' or '+'" - ) - .emit(); - None - } - }; - - let is_rw = output.is_some(); - let is_indirect = constraint_str.contains('*'); - outputs.push(ast::LlvmInlineAsmOutput { - constraint: output.unwrap_or(constraint), - expr, - is_rw, - is_indirect, - }); - } - } - Inputs => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !inputs.is_empty() { - p.eat(&token::Comma); - } - - let constraint = parse_asm_str(&mut p)?; - - if constraint.as_str().starts_with('=') { - struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - p.prev_token.span, - E0662, - "input operand constraint contains '='" - ) - .emit(); - } else if constraint.as_str().starts_with('+') { - struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - p.prev_token.span, - E0663, - "input operand constraint contains '+'" - ) - .emit(); - } - - p.expect(&token::OpenDelim(token::Paren))?; - let input = p.parse_expr()?; - p.expect(&token::CloseDelim(token::Paren))?; - - inputs.push((constraint, input)); - } - } - Clobbers => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !clobs.is_empty() { - p.eat(&token::Comma); - } - - let s = parse_asm_str(&mut p)?; - - if OPTIONS.iter().any(|&opt| s == opt) { - cx.span_warn(p.prev_token.span, "expected a clobber, found an option"); - } else if s.as_str().starts_with('{') || s.as_str().ends_with('}') { - struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - p.prev_token.span, - E0664, - "clobber should not be surrounded by braces" - ) - .emit(); - } - - clobs.push(s); - } - } - Options => { - let option = parse_asm_str(&mut p)?; - - if option == sym::volatile { - // Indicates that the inline assembly has side effects - // and must not be optimized out along with its outputs. - volatile = true; - } else if option == sym::alignstack { - alignstack = true; - } else if option == sym::intel { - dialect = LlvmAsmDialect::Intel; - } else { - cx.span_warn(p.prev_token.span, "unrecognized option"); - } - - if p.token == token::Comma { - p.eat(&token::Comma); - } - } - StateNone => (), - } - - loop { - // MOD_SEP is a double colon '::' without space in between. - // When encountered, the state must be advanced twice. - match (&p.token.kind, state.next(), state.next().next()) { - (&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => { - p.bump(); - break 'statement; - } - (&token::Colon, st, _) | (&token::ModSep, _, st) => { - p.bump(); - state = st; - } - (&token::Eof, ..) => break 'statement, - _ => break, - } - } - } - - Ok(Some(ast::LlvmInlineAsm { - asm, - asm_str_style: asm_str_style.unwrap(), - outputs, - inputs, - clobbers: clobs, - volatile, - alignstack, - dialect, - })) -} diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 6f61e4cba0..c9dd114047 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -56,7 +56,6 @@ pub fn inject( is_proc_macro_crate: bool, has_proc_macro_decls: bool, is_test_crate: bool, - num_crate_types: usize, handler: &rustc_errors::Handler, ) -> ast::Crate { let ecfg = ExpansionConfig::default("proc_macro".to_string()); @@ -81,10 +80,6 @@ pub fn inject( return krate; } - if num_crate_types > 1 { - handler.err("cannot mix `proc-macro` crate type with others"); - } - if is_test_crate { return krate; } diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index e106f6014a..3571517d2b 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -11,7 +11,6 @@ pub fn inject( mut krate: ast::Crate, resolver: &mut dyn ResolverExpand, sess: &Session, - alt_std_name: Option, ) -> ast::Crate { let edition = sess.parse_sess.edition; @@ -53,7 +52,7 @@ pub fn inject( span, ident, vec![cx.attribute(cx.meta_word(span, sym::macro_use))], - ast::ItemKind::ExternCrate(alt_std_name), + ast::ItemKind::ExternCrate(None), ), ); } diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index c08b141b55..d5991be503 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -6,6 +6,7 @@ use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::ptr::P; use rustc_ast_pretty::pprust; +use rustc_errors::Applicability; use rustc_expand::base::*; use rustc_session::Session; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -102,11 +103,21 @@ pub fn expand_test_or_bench( } }; - if let ast::ItemKind::MacCall(_) = item.kind { - cx.sess.parse_sess.span_diagnostic.span_warn( - item.span, - "`#[test]` attribute should not be used on macros. Use `#[cfg(test)]` instead.", - ); + // Note: non-associated fn items are already handled by `expand_test_or_bench` + if !matches!(item.kind, ast::ItemKind::Fn(_)) { + let diag = &cx.sess.parse_sess.span_diagnostic; + let msg = "the `#[test]` attribute may only be used on a non-associated function"; + let mut err = match item.kind { + // These were a warning before #92959 and need to continue being that to avoid breaking + // stable user code (#94508). + ast::ItemKind::MacCall(_) => diag.struct_span_warn(attr_sp, msg), + _ => diag.struct_span_err(attr_sp, msg), + }; + err.span_label(attr_sp, "the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions") + .span_label(item.span, format!("expected a non-associated function, found {} {}", item.kind.article(), item.kind.descr())) + .span_suggestion(attr_sp, "replace with conditional compilation to make the item only exist when tests are being run", String::from("#[cfg(test)]"), Applicability::MaybeIncorrect) + .emit(); + return vec![Annotatable::Item(item)]; } @@ -252,11 +263,6 @@ pub fn expand_test_or_bench( "ignore", cx.expr_bool(sp, should_ignore(&cx.sess, &item)), ), - // allow_fail: true | false - field( - "allow_fail", - cx.expr_bool(sp, should_fail(&cx.sess, &item)), - ), // compile_fail: true | false field("compile_fail", cx.expr_bool(sp, false)), // no_run: true | false @@ -359,10 +365,6 @@ fn should_ignore(sess: &Session, i: &ast::Item) -> bool { sess.contains_name(&i.attrs, sym::ignore) } -fn should_fail(sess: &Session, i: &ast::Item) -> bool { - sess.contains_name(&i.attrs, sym::allow_fail) -} - fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic { match cx.sess.find_by_name(&i.attrs, sym::should_panic) { Some(attr) => { @@ -475,7 +477,7 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { (false, _) => true, } } else { - sd.span_err(i.span, "only functions may be used as tests"); + // should be unreachable because `is_test_fn_item` should catch all non-fn items false } } diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 65e142a00f..faed52727c 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -172,9 +172,9 @@ checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "indexmap" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ "autocfg", "hashbrown", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 3be4250296..2d19040b50 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -19,7 +19,7 @@ gimli = { version = "0.25.0", default-features = false, features = ["write"]} object = { version = "0.27.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } -indexmap = "1.0.2" +indexmap = "1.8.0" libloading = { version = "0.6.0", optional = true } smallvec = "1.6.1" diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 72ebc84c1a..a0550860fa 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -544,7 +544,7 @@ pub(crate) fn codegen_drop<'tcx>( let arg_value = drop_place.place_ref( fx, fx.layout_of(fx.tcx.mk_ref( - &ty::RegionKind::ReErased, + fx.tcx.lifetimes.re_erased, TypeAndMut { ty, mutbl: crate::rustc_hir::Mutability::Mut }, )), ); diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index b0eb3864d8..a099e8b3a6 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -105,8 +105,6 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { Ok(()) } - fn update_symbols(&mut self) {} - fn build(mut self) { enum BuilderKind { Bsd(ar::Builder), diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index b16f5af66f..917afa4eae 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -79,7 +79,7 @@ pub(crate) fn codegen_fn<'tcx>( let arg_uninhabited = fx .mir .args_iter() - .any(|arg| fx.layout_of(fx.monomorphize(&fx.mir.local_decls[arg].ty)).abi.is_uninhabited()); + .any(|arg| fx.layout_of(fx.monomorphize(fx.mir.local_decls[arg].ty)).abi.is_uninhabited()); if !crate::constant::check_constants(&mut fx) { fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]); @@ -668,7 +668,7 @@ fn codegen_stmt<'tcx>( let times = fx .monomorphize(times) .eval(fx.tcx, ParamEnv::reveal_all()) - .val + .val() .try_to_bits(fx.tcx.data_layout.pointer_size) .unwrap(); if operand.layout().size.bytes() == 0 { @@ -749,18 +749,6 @@ fn codegen_stmt<'tcx>( | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) => {} - StatementKind::LlvmInlineAsm(asm) => { - match asm.asm.asm.as_str().trim() { - "" => { - // Black box - } - _ => fx.tcx.sess.span_fatal( - stmt.source_info.span, - "Legacy `llvm_asm!` inline assembly is not supported. \ - Try using the new `asm!` instead.", - ), - } - } StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"), StatementKind::CopyNonOverlapping(inner) => { let dst = codegen_operand(fx, &inner.dst); @@ -830,16 +818,16 @@ pub(crate) fn codegen_place<'tcx>( match cplace.layout().ty.kind() { ty::Array(elem_ty, _len) => { assert!(!from_end, "array subslices are never `from_end`"); - let elem_layout = fx.layout_of(elem_ty); + let elem_layout = fx.layout_of(*elem_ty); let ptr = cplace.to_ptr(); cplace = CPlace::for_ptr( ptr.offset_i64(fx, elem_layout.size.bytes() as i64 * (from as i64)), - fx.layout_of(fx.tcx.mk_array(elem_ty, to - from)), + fx.layout_of(fx.tcx.mk_array(*elem_ty, to - from)), ); } ty::Slice(elem_ty) => { assert!(from_end, "slice subslices should be `from_end`"); - let elem_layout = fx.layout_of(elem_ty); + let elem_layout = fx.layout_of(*elem_ty); let (ptr, len) = cplace.to_ptr_maybe_unsized(); let len = len.unwrap(); cplace = CPlace::for_ptr_with_extra( diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 3b6025c73d..50f98965ab 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -61,7 +61,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option pointer_ty(tcx), ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => { - if has_ptr_meta(tcx, pointee_ty) { + if has_ptr_meta(tcx, *pointee_ty) { return None; } else { pointer_ty(tcx) @@ -100,7 +100,7 @@ fn clif_pair_type_from_ty<'tcx>( (a, b) } ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => { - if has_ptr_meta(tcx, pointee_ty) { + if has_ptr_meta(tcx, *pointee_ty) { (pointer_ty(tcx), pointer_ty(tcx)) } else { return None; diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 9a6c45ae98..274fb211b7 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -46,7 +46,7 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { ConstantKind::Ty(ct) => ct, ConstantKind::Val(..) => continue, }; - match const_.val { + match const_.val() { ConstKind::Value(_) => {} ConstKind::Unevaluated(unevaluated) => { if let Err(err) = @@ -127,13 +127,15 @@ pub(crate) fn codegen_constant<'tcx>( ConstantKind::Ty(ct) => ct, ConstantKind::Val(val, ty) => return codegen_const_value(fx, val, ty), }; - let const_val = match const_.val { + let const_val = match const_.val() { ConstKind::Value(const_val) => const_val, - ConstKind::Unevaluated(uv) if fx.tcx.is_static(uv.def.did) => { - assert!(uv.substs(fx.tcx).is_empty()); - assert!(uv.promoted.is_none()); + ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if fx.tcx.is_static(def.did) => + { + assert!(substs.is_empty()); + assert!(promoted.is_none()); - return codegen_static_ref(fx, uv.def.did, fx.layout_of(const_.ty)).to_cvalue(fx); + return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty())).to_cvalue(fx); } ConstKind::Unevaluated(unevaluated) => { match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { @@ -150,7 +152,7 @@ pub(crate) fn codegen_constant<'tcx>( | ConstKind::Error(_) => unreachable!("{:?}", const_), }; - codegen_const_value(fx, const_val, const_.ty) + codegen_const_value(fx, const_val, const_.ty()) } pub(crate) fn codegen_const_value<'tcx>( @@ -463,7 +465,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( match operand { Operand::Constant(const_) => match const_.literal { ConstantKind::Ty(const_) => { - fx.monomorphize(const_).eval(fx.tcx, ParamEnv::reveal_all()).val.try_to_value() + fx.monomorphize(const_).eval(fx.tcx, ParamEnv::reveal_all()).val().try_to_value() } ConstantKind::Val(val, _) => Some(val), }, @@ -488,7 +490,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( return None; } let const_val = mir_operand_get_const_val(fx, operand)?; - if fx.layout_of(ty).size + if fx.layout_of(*ty).size != const_val.try_to_scalar_int()?.size() { return None; @@ -506,7 +508,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( { return None; } - StatementKind::LlvmInlineAsm(_) | StatementKind::CopyNonOverlapping(_) => { + StatementKind::CopyNonOverlapping(_) => { return None; } // conservative handling StatementKind::Assign(_) diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 638b025be2..693092ba54 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -114,7 +114,7 @@ impl<'tcx> DebugContext<'tcx> { } fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId { - if let Some(type_id) = self.types.get(ty) { + if let Some(type_id) = self.types.get(&ty) { return *type_id; } @@ -143,7 +143,7 @@ impl<'tcx> DebugContext<'tcx> { // Ensure that type is inserted before recursing to avoid duplicates self.types.insert(ty, type_id); - let pointee = self.dwarf_ty(pointee_ty); + let pointee = self.dwarf_ty(*pointee_ty); let type_entry = self.dwarf.unit.get_mut(type_id); @@ -174,7 +174,7 @@ impl<'tcx> DebugContext<'tcx> { field_entry.set( gimli::DW_AT_name, - AttributeValue::String(field_def.ident.as_str().to_string().into_bytes()), + AttributeValue::String(field_def.name.as_str().to_string().into_bytes()), ); field_entry.set( gimli::DW_AT_data_member_location, diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 93384bc551..c242c75ed1 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -6,7 +6,7 @@ use std::fmt::Write; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::mir::InlineAsmOperand; -use rustc_span::Symbol; +use rustc_span::sym; use rustc_target::asm::*; pub(crate) fn codegen_inline_asm<'tcx>( @@ -182,11 +182,7 @@ struct InlineAssemblyGenerator<'a, 'tcx> { impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { fn allocate_registers(&mut self) { let sess = self.tcx.sess; - let map = allocatable_registers( - self.arch, - |feature| sess.target_features.contains(&Symbol::intern(feature)), - &sess.target, - ); + let map = allocatable_registers(self.arch, &sess.target_features, &sess.target); let mut allocated = FxHashMap::<_, (bool, bool)>::default(); let mut regs = vec![None; self.operands.len()]; @@ -319,9 +315,9 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { // Allocate stack slots for saving clobbered registers let abi_clobber = InlineAsmClobberAbi::parse( self.arch, - |feature| self.tcx.sess.target_features.contains(&Symbol::intern(feature)), + &self.tcx.sess.target_features, &self.tcx.sess.target, - Symbol::intern("C"), + sym::C, ) .unwrap() .clobbered_regs(); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index f4703b22ec..55c9b4d9ba 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -90,7 +90,7 @@ macro call_intrinsic_match { match $intrinsic { $( sym::$name => { - assert!($substs.is_noop()); + assert!($substs.is_empty()); if let [$(ref $arg),*] = *$args { let ($($arg,)*) = ( $(codegen_operand($fx, $arg),)* diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index fd96858010..8cae506e0c 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -66,7 +66,7 @@ fn unsize_ptr<'tcx>( (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - (src, unsized_info(fx, a, b, old_info)) + (src, unsized_info(fx, *a, *b, old_info)) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty()); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index f29d13ccab..b016af5174 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -514,7 +514,7 @@ impl<'tcx> CPlace<'tcx> { // Can only happen for vector types let len = u16::try_from(len.eval_usize(fx.tcx, ParamEnv::reveal_all())).unwrap(); - let vector_ty = fx.clif_type(element).unwrap().by(len).unwrap(); + let vector_ty = fx.clif_type(*element).unwrap().by(len).unwrap(); let data = match from.0 { CValueInner::ByRef(ptr, None) => { @@ -721,8 +721,8 @@ impl<'tcx> CPlace<'tcx> { index: Value, ) -> CPlace<'tcx> { let (elem_layout, ptr) = match self.layout().ty.kind() { - ty::Array(elem_ty, _) => (fx.layout_of(elem_ty), self.to_ptr()), - ty::Slice(elem_ty) => (fx.layout_of(elem_ty), self.to_ptr_maybe_unsized().0), + ty::Array(elem_ty, _) => (fx.layout_of(*elem_ty), self.to_ptr()), + ty::Slice(elem_ty) => (fx.layout_of(*elem_ty), self.to_ptr_maybe_unsized().0), _ => bug!("place_index({:?})", self.layout().ty), }; @@ -781,11 +781,11 @@ pub(crate) fn assert_assignable<'tcx>( ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }), ) => { - assert_assignable(fx, a, b); + 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); + assert_assignable(fx, *a, *b); } (ty::FnPtr(_), ty::FnPtr(_)) => { let from_sig = fx.tcx.normalize_erasing_late_bound_regions( diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index 11dd6d49aa..fac532f3e9 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -113,9 +113,6 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { Ok(()) } - fn update_symbols(&mut self) { - } - fn build(mut self) { use std::process::Command; diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 453bcd601d..8a74c4c07e 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -4,9 +4,8 @@ use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -use rustc_hir::LlvmInlineAsmInner; use rustc_middle::{bug, ty::Instance}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use rustc_target::asm::*; use std::borrow::Cow; @@ -106,17 +105,6 @@ enum ConstraintOrRegister { impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { - fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec>>, _inputs: Vec>, span: Span) -> bool { - self.sess().struct_span_err(span, "GCC backend does not support `llvm_asm!`") - .help("consider using the `asm!` macro instead") - .emit(); - - // We return `true` even if we've failed to generate the asm - // because we want to suppress the "malformed inline assembly" error - // generated by the frontend. - true - } - fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], _instance: Instance<'_>, _dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>) { if options.contains(InlineAsmOptions::MAY_UNWIND) { self.sess() @@ -184,7 +172,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let is_target_supported = reg.reg_class().supported_types(asm_arch).iter() .any(|&(_, feature)| { if let Some(feature) = feature { - self.tcx.sess.target_features.contains(&Symbol::intern(feature)) + self.tcx.sess.target_features.contains(&feature) } else { true // Register class is unconditionally supported } @@ -572,6 +560,7 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => unimplemented!(), + InlineAsmRegClass::Msp430(_) => unimplemented!(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => unimplemented!(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => unimplemented!(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => unimplemented!(), @@ -634,6 +623,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::Msp430(_) => unimplemented!(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(), @@ -741,6 +731,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option InlineAsmRegClass::Bpf(_) => unimplemented!(), InlineAsmRegClass::Hexagon(_) => unimplemented!(), InlineAsmRegClass::Mips(_) => unimplemented!(), + InlineAsmRegClass::Msp430(_) => unimplemented!(), InlineAsmRegClass::Nvptx(_) => unimplemented!(), InlineAsmRegClass::PowerPC(_) => unimplemented!(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 379c88bbd4..ffb77e16a1 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1256,7 +1256,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { aggregate_value } - fn landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>, _num_clauses: usize) -> RValue<'gcc> { + fn set_personality_fn(&mut self, _personality: RValue<'gcc>) { + // TODO(antoyo) + } + + fn cleanup_landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>) -> RValue<'gcc> { let field1 = self.context.new_field(None, self.u8_type, "landing_pad_field_1"); let field2 = self.context.new_field(None, self.i32_type, "landing_pad_field_1"); let struct_type = self.context.new_struct_type(None, "landing_pad", &[field1, field2]); @@ -1267,11 +1271,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // rustc_codegen_ssa now calls the unwinding builder methods even on panic=abort. } - fn set_cleanup(&mut self, _landing_pad: RValue<'gcc>) { - // TODO(antoyo) - } - - fn resume(&mut self, _exn: RValue<'gcc>) -> RValue<'gcc> { + fn resume(&mut self, _exn: RValue<'gcc>) { unimplemented!(); } @@ -1279,7 +1279,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { unimplemented!(); } - fn cleanup_ret(&mut self, _funclet: &Funclet, _unwind: Option>) -> RValue<'gcc> { + fn cleanup_ret(&mut self, _funclet: &Funclet, _unwind: Option>) { unimplemented!(); } @@ -1287,18 +1287,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { unimplemented!(); } - fn catch_switch(&mut self, _parent: Option>, _unwind: Option>, _num_handlers: usize) -> RValue<'gcc> { + fn catch_switch( + &mut self, + _parent: Option>, + _unwind: Option>, + _handlers: &[Block<'gcc>], + ) -> RValue<'gcc> { unimplemented!(); } - fn add_handler(&mut self, _catch_switch: RValue<'gcc>, _handler: Block<'gcc>) { - unimplemented!(); - } - - fn set_personality_fn(&mut self, _personality: RValue<'gcc>) { - // TODO(antoyo) - } - // Atomic Operations fn atomic_cmpxchg(&mut self, dst: RValue<'gcc>, cmp: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> { let expected = self.current_func().new_local(None, cmp.get_type(), "expected"); diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index ba4589bd81..ddc2b88191 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -144,7 +144,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { // TODO(antoyo): set link section. } - if attrs.flags.contains(CodegenFnAttrFlags::USED) { + if attrs.flags.contains(CodegenFnAttrFlags::USED) || attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { self.add_used_global(global.to_rvalue()); } } diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 9c39c8f91a..281e49fa8a 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -57,7 +57,7 @@ pub fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLa (layout.ty.kind(), &layout.variants) { if def.is_enum() && !def.variants.is_empty() { - write!(&mut name, "::{}", def.variants[index].ident).unwrap(); + write!(&mut name, "::{}", def.variants[index].name).unwrap(); } } if let (&ty::Generator(_, _, _), &Variants::Single { index }) = diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index e9b66b54c5..8a11e3e71b 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -37,7 +37,7 @@ impl ArgAttributeExt for ArgAttribute { where F: FnMut(llvm::Attribute), { - for_each_kind!(self, f, NoAlias, NoCapture, NonNull, ReadOnly, InReg) + for_each_kind!(self, f, NoAlias, NoCapture, NonNull, ReadOnly, InReg, NoUndef) } } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 30d91b41a8..7680d4fd23 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -64,7 +64,7 @@ pub(crate) unsafe fn codegen( llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } if tcx.sess.must_emit_unwind_tables() { - attributes::emit_uwtable(llfn, true); + attributes::emit_uwtable(llfn); } let callee = kind.fn_name(method.name); @@ -111,7 +111,7 @@ pub(crate) unsafe fn codegen( llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } if tcx.sess.must_emit_unwind_tables() { - attributes::emit_uwtable(llfn, true); + attributes::emit_uwtable(llfn); } let kind = if has_alloc_error_handler { AllocatorKind::Global } else { AllocatorKind::Default }; diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index caf16c1939..e22bec2495 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -7,16 +7,13 @@ use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use rustc_ast::LlvmAsmDialect; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; -use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, span_bug, ty::Instance}; -use rustc_span::{Pos, Span, Symbol}; +use rustc_span::{Pos, Span}; use rustc_target::abi::*; use rustc_target::asm::*; @@ -24,100 +21,6 @@ use libc::{c_char, c_uint}; use tracing::debug; impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { - fn codegen_llvm_inline_asm( - &mut self, - ia: &hir::LlvmInlineAsmInner, - outputs: Vec>, - mut inputs: Vec<&'ll Value>, - span: Span, - ) -> bool { - let mut ext_constraints = vec![]; - let mut output_types = vec![]; - - // Prepare the output operands - let mut indirect_outputs = vec![]; - for (i, (out, &place)) in ia.outputs.iter().zip(&outputs).enumerate() { - if out.is_rw { - let operand = self.load_operand(place); - if let OperandValue::Immediate(_) = operand.val { - inputs.push(operand.immediate()); - } - ext_constraints.push(i.to_string()); - } - if out.is_indirect { - let operand = self.load_operand(place); - if let OperandValue::Immediate(_) = operand.val { - indirect_outputs.push(operand.immediate()); - } - } else { - output_types.push(place.layout.llvm_type(self.cx)); - } - } - if !indirect_outputs.is_empty() { - indirect_outputs.extend_from_slice(&inputs); - inputs = indirect_outputs; - } - - let clobbers = ia.clobbers.iter().map(|s| format!("~{{{}}}", &s)); - - // Default per-arch clobbers - // Basically what clang does - let arch_clobbers = match &self.sess().target.arch[..] { - "x86" | "x86_64" => &["~{dirflag}", "~{fpsr}", "~{flags}"][..], - "mips" | "mips64" => &["~{$1}"], - _ => &[], - }; - - let all_constraints = ia - .outputs - .iter() - .map(|out| out.constraint.to_string()) - .chain(ia.inputs.iter().map(|s| s.to_string())) - .chain(ext_constraints) - .chain(clobbers) - .chain(arch_clobbers.iter().map(|s| (*s).to_string())) - .collect::>() - .join(","); - - debug!("Asm Constraints: {}", &all_constraints); - - // Depending on how many outputs we have, the return type is different - let num_outputs = output_types.len(); - let output_type = match num_outputs { - 0 => self.type_void(), - 1 => output_types[0], - _ => self.type_struct(&output_types, false), - }; - - let asm = ia.asm.as_str(); - let r = inline_asm_call( - self, - &asm, - &all_constraints, - &inputs, - output_type, - ia.volatile, - ia.alignstack, - ia.dialect, - &[span], - false, - None, - ); - if r.is_none() { - return false; - } - let r = r.unwrap(); - - // Again, based on how many outputs we have - let outputs = ia.outputs.iter().zip(&outputs).filter(|&(o, _)| !o.is_indirect); - for (i, (_, &place)) in outputs.enumerate() { - let v = if num_outputs == 1 { r } else { self.extract_value(r, i as u64) }; - OperandValue::Immediate(v).store(self, place); - } - - true - } - fn codegen_inline_asm( &mut self, template: &[InlineAsmTemplatePiece], @@ -142,9 +45,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { for &(_, feature) in reg_class.supported_types(asm_arch) { if let Some(feature) = feature { let codegen_fn_attrs = self.tcx.codegen_fn_attrs(instance.def_id()); - let feature_name = Symbol::intern(feature); - if self.tcx.sess.target_features.contains(&feature_name) - || codegen_fn_attrs.target_features.contains(&feature_name) + if self.tcx.sess.target_features.contains(&feature) + || codegen_fn_attrs.target_features.contains(&feature) { return true; } @@ -330,6 +232,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { InlineAsmArch::SpirV => {} InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {} InlineAsmArch::Bpf => {} + InlineAsmArch::Msp430 => { + constraints.push("~{sr}".to_string()); + } } } if !options.contains(InlineAsmOptions::NOMEM) { @@ -349,9 +254,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { InlineAsmArch::X86 | InlineAsmArch::X86_64 if !options.contains(InlineAsmOptions::ATT_SYNTAX) => { - LlvmAsmDialect::Intel + llvm::AsmDialect::Intel } - _ => LlvmAsmDialect::Att, + _ => llvm::AsmDialect::Att, }; let result = inline_asm_call( self, @@ -455,7 +360,7 @@ pub(crate) fn inline_asm_call<'ll>( output: &'ll llvm::Type, volatile: bool, alignstack: bool, - dia: LlvmAsmDialect, + dia: llvm::AsmDialect, line_spans: &[Span], unwind: bool, dest_catch_funclet: Option<( @@ -498,7 +403,7 @@ pub(crate) fn inline_asm_call<'ll>( cons.len(), volatile, alignstack, - llvm::AsmDialect::from_generic(dia), + dia, can_throw, ); @@ -522,7 +427,7 @@ pub(crate) fn inline_asm_call<'ll>( // we just encode the start position of each line. // FIXME: Figure out a way to pass the entire line spans. let mut srcloc = vec![]; - if dia == LlvmAsmDialect::Intel && line_spans.len() > 1 { + if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { // LLVM inserts an extra line to add the ".intel_syntax", so add // a dummy srcloc entry for it. // @@ -678,6 +583,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e", InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r", InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r", InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } @@ -764,6 +670,7 @@ fn modifier_to_llvm( }, InlineAsmRegClass::Avr(_) => None, InlineAsmRegClass::S390x(_) => None, + InlineAsmRegClass::Msp430(_) => None, InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } @@ -832,6 +739,7 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(), InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(), + InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(), InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 7f82ce307d..f6d7221d4e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -55,12 +55,28 @@ pub fn sanitize<'ll>(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: & if enabled.contains(SanitizerSet::HWADDRESS) { llvm::Attribute::SanitizeHWAddress.apply_llfn(Function, llfn); } + if enabled.contains(SanitizerSet::MEMTAG) { + // Check to make sure the mte target feature is actually enabled. + let sess = cx.tcx.sess; + let features = llvm_util::llvm_global_features(sess).join(","); + let mte_feature_enabled = features.rfind("+mte"); + let mte_feature_disabled = features.rfind("-mte"); + + if mte_feature_enabled.is_none() || (mte_feature_disabled > mte_feature_enabled) { + sess.err("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`"); + } + + llvm::Attribute::SanitizeMemTag.apply_llfn(Function, llfn); + } } /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. #[inline] -pub fn emit_uwtable(val: &Value, emit: bool) { - Attribute::UWTable.toggle_llfn(Function, val, emit); +pub fn emit_uwtable(val: &Value) { + // NOTE: We should determine if we even need async unwind tables, as they + // take have more overhead and if we can use sync unwind tables we + // probably should. + llvm::EmitUWTableAttr(val, true); } /// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue. @@ -275,7 +291,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( // You can also find more info on why Windows always requires uwtables here: // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 if cx.sess().must_emit_unwind_tables() { - attributes::emit_uwtable(llfn, true); + attributes::emit_uwtable(llfn); } if cx.sess().opts.debugging_opts.profile_sample_use.is_some() { @@ -322,12 +338,33 @@ pub fn from_fn_attrs<'ll, 'tcx>( // The target doesn't care; the subtarget reads our attribute. apply_tune_cpu_attr(cx, llfn); - let mut function_features = codegen_fn_attrs - .target_features + let function_features = + codegen_fn_attrs.target_features.iter().map(|f| f.as_str()).collect::>(); + + if let Some(f) = llvm_util::check_tied_features( + cx.tcx.sess, + &function_features.iter().map(|f| (*f, true)).collect(), + ) { + let span = cx + .tcx + .get_attrs(instance.def_id()) + .iter() + .find(|a| a.has_name(rustc_span::sym::target_feature)) + .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span); + let msg = format!( + "the target features {} must all be either enabled or disabled together", + f.join(", ") + ); + let mut err = cx.tcx.sess.struct_span_err(span, &msg); + err.help("add the missing features in a `target_feature` attribute"); + err.emit(); + return; + } + + let mut function_features = function_features .iter() - .flat_map(|f| { - let feature = f.as_str(); - llvm_util::to_llvm_feature(cx.tcx.sess, feature) + .flat_map(|feat| { + llvm_util::to_llvm_feature(cx.tcx.sess, feat) .into_iter() .map(|f| format!("+{}", f)) .collect::>() diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 2fb5a0f9fa..21bd1dae7a 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -1,6 +1,7 @@ //! A helper class for dealing with static archives -use std::ffi::{CStr, CString}; +use std::env; +use std::ffi::{CStr, CString, OsString}; use std::io; use std::mem; use std::path::{Path, PathBuf}; @@ -26,7 +27,6 @@ pub struct LlvmArchiveBuilder<'a> { config: ArchiveConfig<'a>, removals: Vec, additions: Vec, - should_update_symbols: bool, src_archive: Option>, } @@ -74,7 +74,6 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { config, removals: Vec::new(), additions: Vec::new(), - should_update_symbols: false, src_archive: None, } } @@ -128,12 +127,6 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { .push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() }); } - /// Indicate that the next call to `build` should update all symbols in - /// the archive (equivalent to running 'ar s' over it). - fn update_symbols(&mut self) { - self.should_update_symbols = true; - } - /// Combine the provided files, rlibs, and native libraries into a single /// `Archive`. fn build(mut self) { @@ -158,54 +151,127 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { output_path.with_extension("lib") }; - // we've checked for \0 characters in the library name already - let dll_name_z = CString::new(lib_name).unwrap(); - // All import names are Rust identifiers and therefore cannot contain \0 characters. - // FIXME: when support for #[link_name] implemented, ensure that import.name values don't - // have any \0 characters - let import_name_and_ordinal_vector: Vec<(CString, Option)> = dll_imports + let mingw_gnu_toolchain = self.config.sess.target.llvm_target.ends_with("pc-windows-gnu"); + + let import_name_and_ordinal_vector: Vec<(String, Option)> = dll_imports .iter() .map(|import: &DllImport| { if self.config.sess.target.arch == "x86" { - (LlvmArchiveBuilder::i686_decorated_name(import), import.ordinal) + ( + LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain), + import.ordinal, + ) } else { - (CString::new(import.name.to_string()).unwrap(), import.ordinal) + (import.name.to_string(), import.ordinal) } }) .collect(); - let output_path_z = rustc_fs_util::path_to_c_string(&output_path); + if mingw_gnu_toolchain { + // The binutils linker used on -windows-gnu targets cannot read the import + // libraries generated by LLVM: in our attempts, the linker produced an .EXE + // that loaded but crashed with an AV upon calling one of the imported + // functions. Therefore, use binutils to create the import library instead, + // by writing a .DEF file to the temp dir and calling binutils's dlltool. + let def_file_path = + tmpdir.as_ref().join(format!("{}_imports", lib_name)).with_extension("def"); + + let def_file_content = format!( + "EXPORTS\n{}", + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| { + match ordinal { + Some(n) => format!("{} @{} NONAME", name, n), + None => name, + } + }) + .collect::>() + .join("\n") + ); - tracing::trace!("invoking LLVMRustWriteImportLibrary"); - tracing::trace!(" dll_name {:#?}", dll_name_z); - tracing::trace!(" output_path {}", output_path.display()); - tracing::trace!( - " import names: {}", - dll_imports.iter().map(|import| import.name.to_string()).collect::>().join(", "), - ); + match std::fs::write(&def_file_path, def_file_content) { + Ok(_) => {} + Err(e) => { + self.config.sess.fatal(&format!("Error writing .DEF file: {}", e)); + } + }; - let ffi_exports: Vec = import_name_and_ordinal_vector - .iter() - .map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal)) - .collect(); - let result = unsafe { - crate::llvm::LLVMRustWriteImportLibrary( - dll_name_z.as_ptr(), - output_path_z.as_ptr(), - ffi_exports.as_ptr(), - ffi_exports.len(), - llvm_machine_type(&self.config.sess.target.arch) as u16, - !self.config.sess.target.is_like_msvc, - ) - }; + let dlltool = find_binutils_dlltool(self.config.sess); + let result = std::process::Command::new(dlltool) + .args([ + "-d", + def_file_path.to_str().unwrap(), + "-D", + lib_name, + "-l", + output_path.to_str().unwrap(), + ]) + .output(); + + match result { + Err(e) => { + self.config.sess.fatal(&format!("Error calling dlltool: {}", e)); + } + Ok(output) if !output.status.success() => self.config.sess.fatal(&format!( + "Dlltool could not create import library: {}\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + )), + _ => {} + } + } else { + // we've checked for \0 characters in the library name already + let dll_name_z = CString::new(lib_name).unwrap(); + + let output_path_z = rustc_fs_util::path_to_c_string(&output_path); + + tracing::trace!("invoking LLVMRustWriteImportLibrary"); + tracing::trace!(" dll_name {:#?}", dll_name_z); + tracing::trace!(" output_path {}", output_path.display()); + tracing::trace!( + " import names: {}", + dll_imports + .iter() + .map(|import| import.name.to_string()) + .collect::>() + .join(", "), + ); - if result == crate::llvm::LLVMRustResult::Failure { - self.config.sess.fatal(&format!( - "Error creating import library for {}: {}", - lib_name, - llvm::last_error().unwrap_or("unknown LLVM error".to_string()) - )); - } + // All import names are Rust identifiers and therefore cannot contain \0 characters. + // FIXME: when support for #[link_name] is implemented, ensure that the import names + // still don't contain any \0 characters. Also need to check that the names don't + // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved + // in definition files. + let cstring_import_name_and_ordinal_vector: Vec<(CString, Option)> = + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| (CString::new(name).unwrap(), ordinal)) + .collect(); + + let ffi_exports: Vec = cstring_import_name_and_ordinal_vector + .iter() + .map(|(name_z, ordinal)| LLVMRustCOFFShortExport::new(name_z.as_ptr(), *ordinal)) + .collect(); + let result = unsafe { + crate::llvm::LLVMRustWriteImportLibrary( + dll_name_z.as_ptr(), + output_path_z.as_ptr(), + ffi_exports.as_ptr(), + ffi_exports.len(), + llvm_machine_type(&self.config.sess.target.arch) as u16, + !self.config.sess.target.is_like_msvc, + ) + }; + + if result == crate::llvm::LLVMRustResult::Failure { + self.config.sess.fatal(&format!( + "Error creating import library for {}: {}", + lib_name, + llvm::last_error().unwrap_or("unknown LLVM error".to_string()) + )); + } + }; self.add_archive(&output_path, |_| false).unwrap_or_else(|e| { self.config.sess.fatal(&format!( @@ -239,7 +305,6 @@ impl<'a> LlvmArchiveBuilder<'a> { let mut members = Vec::new(); let dst = CString::new(self.config.dst.to_str().unwrap())?; - let should_update_symbols = self.should_update_symbols; unsafe { if let Some(archive) = self.src_archive() { @@ -311,7 +376,7 @@ impl<'a> LlvmArchiveBuilder<'a> { dst.as_ptr(), members.len() as libc::size_t, members.as_ptr() as *const &_, - should_update_symbols, + true, kind, ); let ret = if r.into_result().is_err() { @@ -332,22 +397,61 @@ impl<'a> LlvmArchiveBuilder<'a> { } } - fn i686_decorated_name(import: &DllImport) -> CString { + fn i686_decorated_name(import: &DllImport, mingw: bool) -> String { let name = import.name; - // We verified during construction that `name` does not contain any NULL characters, so the - // conversion to CString is guaranteed to succeed. - CString::new(match import.calling_convention { - DllCallingConvention::C => format!("_{}", name), - DllCallingConvention::Stdcall(arg_list_size) => format!("_{}@{}", name, arg_list_size), + let prefix = if mingw { "" } else { "_" }; + + match import.calling_convention { + DllCallingConvention::C => format!("{}{}", prefix, name), + DllCallingConvention::Stdcall(arg_list_size) => { + format!("{}{}@{}", prefix, name, arg_list_size) + } DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size), DllCallingConvention::Vectorcall(arg_list_size) => { format!("{}@@{}", name, arg_list_size) } - }) - .unwrap() + } } } fn string_to_io_error(s: String) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) } + +fn find_binutils_dlltool(sess: &Session) -> OsString { + assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); + if let Some(dlltool_path) = &sess.opts.debugging_opts.dlltool { + return dlltool_path.clone().into_os_string(); + } + + let mut tool_name: OsString = if sess.host.arch != sess.target.arch { + // We are cross-compiling, so we need the tool with the prefix matching our target + if sess.target.arch == "x86" { + "i686-w64-mingw32-dlltool" + } else { + "x86_64-w64-mingw32-dlltool" + } + } else { + // We are not cross-compiling, so we just want `dlltool` + "dlltool" + } + .into(); + + if sess.host.options.is_like_windows { + // If we're compiling on Windows, add the .exe suffix + tool_name.push(".exe"); + } + + // NOTE: it's not clear how useful it is to explicitly search PATH. + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&tool_name); + if full_path.is_file() { + return full_path.into_os_string(); + } + } + + // The user didn't specify the location of the dlltool binary, and we weren't able + // to find the appropriate one on the PATH. Just return the name of the tool + // and let the invocation fail with a hopefully useful error message. + tool_name +} diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index ddba43cd1f..6afa649b6d 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -349,13 +349,6 @@ fn fat_lto( ); save_temp_bitcode(cgcx, &module, "lto.after-restriction"); } - - if cgcx.no_landing_pads { - unsafe { - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); - } - save_temp_bitcode(cgcx, &module, "lto.after-nounwind"); - } } Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode }) @@ -770,16 +763,6 @@ pub unsafe fn optimize_thin_module( return Err(write::llvm_err(&diag_handler, msg)); } - // Like with "fat" LTO, get some better optimizations if landing pads - // are disabled by removing all landing pads. - if cgcx.no_landing_pads { - let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_thin_lto_remove_landing_pads", thin_module.name()); - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); - save_temp_bitcode(cgcx, &module, "thin-lto-after-nounwind"); - } - // Up next comes the per-module local analyses that we do for Thin LTO. // Each of these functions is basically copied from the LLVM // implementation and then tailored to suit this implementation. Ideally diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5217fa2758..c9a04e6280 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -731,27 +731,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - if !self.fptoint_sat_broken_in_llvm() { - let src_ty = self.cx.val_ty(val); - let float_width = self.cx.float_width(src_ty); - let int_width = self.cx.int_width(dest_ty); - let name = format!("llvm.fptoui.sat.i{}.f{}", int_width, float_width); - return Some(self.call_intrinsic(&name, &[val])); - } - - None + self.fptoint_sat(false, val, dest_ty) } fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - if !self.fptoint_sat_broken_in_llvm() { - let src_ty = self.cx.val_ty(val); - let float_width = self.cx.float_width(src_ty); - let int_width = self.cx.int_width(dest_ty); - let name = format!("llvm.fptosi.sat.i{}.f{}", int_width, float_width); - return Some(self.call_intrinsic(&name, &[val])); - } - - None + self.fptoint_sat(true, val, dest_ty) } fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { @@ -972,29 +956,24 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, UNNAMED) } } - fn landing_pad( - &mut self, - ty: &'ll Type, - pers_fn: &'ll Value, - num_clauses: usize, - ) -> &'ll Value { - // Use LLVMSetPersonalityFn to set the personality. It supports arbitrary Consts while, - // LLVMBuildLandingPad requires the argument to be a Function (as of LLVM 12). The - // personality lives on the parent function anyway. - self.set_personality_fn(pers_fn); + fn set_personality_fn(&mut self, personality: &'ll Value) { unsafe { - llvm::LLVMBuildLandingPad(self.llbuilder, ty, None, num_clauses as c_uint, UNNAMED) + llvm::LLVMSetPersonalityFn(self.llfn(), personality); } } - fn set_cleanup(&mut self, landing_pad: &'ll Value) { + fn cleanup_landing_pad(&mut self, ty: &'ll Type, pers_fn: &'ll Value) -> &'ll Value { + let landing_pad = self.landing_pad(ty, pers_fn, 1 /* FIXME should this be 0? */); unsafe { llvm::LLVMSetCleanup(landing_pad, llvm::True); } + landing_pad } - fn resume(&mut self, exn: &'ll Value) -> &'ll Value { - unsafe { llvm::LLVMBuildResume(self.llbuilder, exn) } + fn resume(&mut self, exn: &'ll Value) { + unsafe { + llvm::LLVMBuildResume(self.llbuilder, exn); + } } fn cleanup_pad(&mut self, parent: Option<&'ll Value>, args: &[&'ll Value]) -> Funclet<'ll> { @@ -1011,14 +990,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { Funclet::new(ret.expect("LLVM does not have support for cleanuppad")) } - fn cleanup_ret( - &mut self, - funclet: &Funclet<'ll>, - unwind: Option<&'ll BasicBlock>, - ) -> &'ll Value { - let ret = - unsafe { llvm::LLVMRustBuildCleanupRet(self.llbuilder, funclet.cleanuppad(), unwind) }; - ret.expect("LLVM does not have support for cleanupret") + fn cleanup_ret(&mut self, funclet: &Funclet<'ll>, unwind: Option<&'ll BasicBlock>) { + unsafe { + llvm::LLVMRustBuildCleanupRet(self.llbuilder, funclet.cleanuppad(), unwind) + .expect("LLVM does not have support for cleanupret"); + } } fn catch_pad(&mut self, parent: &'ll Value, args: &[&'ll Value]) -> Funclet<'ll> { @@ -1039,7 +1015,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { &mut self, parent: Option<&'ll Value>, unwind: Option<&'ll BasicBlock>, - num_handlers: usize, + handlers: &[&'ll BasicBlock], ) -> &'ll Value { let name = cstr!("catchswitch"); let ret = unsafe { @@ -1047,23 +1023,17 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { self.llbuilder, parent, unwind, - num_handlers as c_uint, + handlers.len() as c_uint, name.as_ptr(), ) }; - ret.expect("LLVM does not have support for catchswitch") - } - - fn add_handler(&mut self, catch_switch: &'ll Value, handler: &'ll BasicBlock) { - unsafe { - llvm::LLVMRustAddHandler(catch_switch, handler); - } - } - - fn set_personality_fn(&mut self, personality: &'ll Value) { - unsafe { - llvm::LLVMSetPersonalityFn(self.llfn(), personality); + let ret = ret.expect("LLVM does not have support for catchswitch"); + for handler in handlers { + unsafe { + llvm::LLVMRustAddHandler(ret, handler); + } } + ret } // Atomic Operations @@ -1455,4 +1425,58 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { _ => false, } } + + fn fptoint_sat( + &mut self, + signed: bool, + val: &'ll Value, + dest_ty: &'ll Type, + ) -> Option<&'ll Value> { + if !self.fptoint_sat_broken_in_llvm() { + let src_ty = self.cx.val_ty(val); + let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector + { + assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty)); + ( + self.cx.element_type(src_ty), + self.cx.element_type(dest_ty), + Some(self.cx.vector_length(src_ty)), + ) + } else { + (src_ty, dest_ty, None) + }; + let float_width = self.cx.float_width(float_ty); + let int_width = self.cx.int_width(int_ty); + + let instr = if signed { "fptosi" } else { "fptoui" }; + let name = if let Some(vector_length) = vector_length { + format!( + "llvm.{}.sat.v{}i{}.v{}f{}", + instr, vector_length, int_width, vector_length, float_width + ) + } else { + format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width) + }; + let f = + self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); + Some(self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None)) + } else { + None + } + } + + pub(crate) fn landing_pad( + &mut self, + ty: &'ll Type, + pers_fn: &'ll Value, + num_clauses: usize, + ) -> &'ll Value { + // Use LLVMSetPersonalityFn to set the personality. It supports arbitrary Consts while, + // LLVMBuildLandingPad requires the argument to be a Function (as of LLVM 12). The + // personality lives on the parent function anyway. + self.set_personality_fn(pers_fn); + unsafe { + llvm::LLVMBuildLandingPad(self.llbuilder, ty, None, num_clauses as c_uint, UNNAMED) + } + } } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index d43c7c6065..6707de9335 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -522,6 +522,9 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { } if attrs.flags.contains(CodegenFnAttrFlags::USED) { + // `USED` and `USED_LINKER` can't be used together. + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)); + // The semantics of #[used] in Rust only require the symbol to make it into the // object file. It is explicitly allowed for the linker to strip the symbol if it // is dead. As such, use llvm.compiler.used instead of llvm.used. @@ -530,6 +533,12 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { // in some versions of the gold linker. self.add_compiler_used_global(g); } + if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { + // `USED` and `USED_LINKER` can't be used together. + assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED)); + + self.add_used_global(g); + } } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 9f24a95482..ddc8d72e9b 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -21,7 +21,8 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_session::config::{CFGuard, CrateType, DebugInfo}; +use rustc_session::config::{BranchProtection, CFGuard, CFProtection}; +use rustc_session::config::{CrateType, DebugInfo, PAuthKey, PacRet}; use rustc_session::Session; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -134,7 +135,8 @@ pub unsafe fn create_module<'ll>( let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); let mut target_data_layout = sess.target.data_layout.clone(); - if llvm_util::get_version() < (13, 0, 0) { + let llvm_version = llvm_util::get_version(); + if llvm_version < (13, 0, 0) { if sess.target.arch == "powerpc64" { target_data_layout = target_data_layout.replace("-S128", ""); } @@ -145,6 +147,18 @@ pub unsafe fn create_module<'ll>( target_data_layout = "e-m:e-p:64:64-i64:64-n32:64-S128".to_string(); } } + if llvm_version < (14, 0, 0) { + if sess.target.llvm_target == "i686-pc-windows-msvc" + || sess.target.llvm_target == "i586-pc-windows-msvc" + { + target_data_layout = + "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:32-n8:16:32-a:0:32-S32" + .to_string(); + } + if sess.target.arch == "wasm32" { + target_data_layout = target_data_layout.replace("-p10:8:8-p20:8:8", ""); + } + } // Ensure the data-layout values hardcoded remain the defaults. if sess.target.is_builtin { @@ -215,16 +229,19 @@ pub unsafe fn create_module<'ll>( // to ensure intrinsic calls don't use it. if !sess.needs_plt() { let avoid_plt = "RtLibUseGOT\0".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1); + llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); } if sess.is_sanitizer_cfi_enabled() { // FIXME(rcvalle): Add support for non canonical jump tables. let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast(); - // FIXME(rcvalle): Add it with Override behavior flag--LLVMRustAddModuleFlag adds it with - // Warning behavior flag. Add support for specifying the behavior flag to - // LLVMRustAddModuleFlag. - llvm::LLVMRustAddModuleFlag(llmod, canonical_jump_tables, 1); + // FIXME(rcvalle): Add it with Override behavior flag. + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Warning, + canonical_jump_tables, + 1, + ); } // Control Flow Guard is currently only supported by the MSVC linker on Windows. @@ -233,15 +250,75 @@ pub unsafe fn create_module<'ll>( CFGuard::Disabled => {} CFGuard::NoChecks => { // Set `cfguard=1` module flag to emit metadata only. - llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 1) + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Warning, + "cfguard\0".as_ptr() as *const _, + 1, + ) } CFGuard::Checks => { // Set `cfguard=2` module flag to emit metadata and checks. - llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 2) + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Warning, + "cfguard\0".as_ptr() as *const _, + 2, + ) } } } + if let Some(BranchProtection { bti, pac_ret }) = sess.opts.debugging_opts.branch_protection { + if sess.target.arch != "aarch64" { + sess.err("-Zbranch-protection is only supported on aarch64"); + } else { + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Error, + "branch-target-enforcement\0".as_ptr().cast(), + bti.into(), + ); + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Error, + "sign-return-address\0".as_ptr().cast(), + pac_ret.is_some().into(), + ); + let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Error, + "sign-return-address-all\0".as_ptr().cast(), + pac_opts.leaf.into(), + ); + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Error, + "sign-return-address-with-bkey\0".as_ptr().cast(), + u32::from(pac_opts.key == PAuthKey::B), + ); + } + } + + // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang). + if let CFProtection::Branch | CFProtection::Full = sess.opts.debugging_opts.cf_protection { + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Override, + "cf-protection-branch\0".as_ptr().cast(), + 1, + ) + } + if let CFProtection::Return | CFProtection::Full = sess.opts.debugging_opts.cf_protection { + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Override, + "cf-protection-return\0".as_ptr().cast(), + 1, + ) + } + llmod } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index e0af565375..58f391692c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -5,12 +5,14 @@ use crate::llvm; use llvm::coverageinfo::CounterMappingRegion; use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression}; use rustc_codegen_ssa::traits::{ConstMethods, CoverageInfoMethods}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; -use rustc_hir::def_id::{DefId, DefIdSet}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefIdSet; use rustc_llvm::RustString; +use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::CodeRegion; use rustc_middle::ty::TyCtxt; -use rustc_span::Symbol; use std::ffi::CString; @@ -37,7 +39,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { // LLVM 12. let version = coverageinfo::mapping_version(); if version < 4 { - tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 12 or higher."); + tcx.sess.fatal("rustc option `-C instrument-coverage` requires LLVM 12 or higher."); } debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); @@ -46,7 +48,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { // functions exist. Generate synthetic functions with a (required) single counter, and add the // MIR `Coverage` code regions to the `function_coverage_map`, before calling // `ctx.take_function_coverage_map()`. - if !tcx.sess.instrument_coverage_except_unused_functions() { + if cx.codegen_unit.is_code_coverage_dead_code_cgu() { add_unused_functions(cx); } @@ -75,10 +77,18 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { let coverage_mapping_buffer = llvm::build_byte_buffer(|coverage_mapping_buffer| { mapgen.write_coverage_mapping(expressions, counter_regions, coverage_mapping_buffer); }); - debug_assert!( - !coverage_mapping_buffer.is_empty(), - "Every `FunctionCoverage` should have at least one counter" - ); + + if coverage_mapping_buffer.is_empty() { + if function_coverage.is_used() { + bug!( + "A used function should have had coverage mapping data but did not: {}", + mangled_function_name + ); + } else { + debug!("unused function had no coverage mapping data: {}", mangled_function_name); + continue; + } + } function_data.push((mangled_function_name, source_hash, is_used, coverage_mapping_buffer)); } @@ -264,33 +274,42 @@ fn save_function_record( /// (functions referenced by other "used" or public items). Any other functions considered unused, /// or "Unreachable", were still parsed and processed through the MIR stage, but were not /// codegenned. (Note that `-Clink-dead-code` can force some unused code to be codegenned, but -/// that flag is known to cause other errors, when combined with `-Z instrument-coverage`; and +/// that flag is known to cause other errors, when combined with `-C instrument-coverage`; and /// `-Clink-dead-code` will not generate code for unused generic functions.) /// /// We can find the unused functions (including generic functions) by the set difference of all MIR /// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query /// `codegened_and_inlined_items`). /// -/// *HOWEVER* the codegenned `DefId`s are partitioned across multiple `CodegenUnit`s (CGUs), and -/// this function is processing a `function_coverage_map` for the functions (`Instance`/`DefId`) -/// allocated to only one of those CGUs. We must NOT inject any unused functions's `CodeRegion`s -/// more than once, so we have to pick a CGUs `function_coverage_map` into which the unused -/// function will be inserted. +/// These unused functions are then codegen'd in one of the CGUs which is marked as the +/// "code coverage dead code cgu" during the partitioning process. This prevents us from generating +/// code regions for the same function more than once which can lead to linker errors regarding +/// duplicate symbols. fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { - let tcx = cx.tcx; + assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); - // FIXME(#79622): Can this solution be simplified and/or improved? Are there other sources - // of compiler state data that might help (or better sources that could be exposed, but - // aren't yet)? + let tcx = cx.tcx; let ignore_unused_generics = tcx.sess.instrument_coverage_except_unused_generics(); - let all_def_ids: DefIdSet = tcx + let eligible_def_ids: DefIdSet = tcx .mir_keys(()) .iter() .filter_map(|local_def_id| { let def_id = local_def_id.to_def_id(); - if ignore_unused_generics && tcx.generics_of(def_id).requires_monomorphization(tcx) { + let kind = tcx.def_kind(def_id); + // `mir_keys` will give us `DefId`s for all kinds of things, not + // just "functions", like consts, statics, etc. Filter those out. + // If `ignore_unused_generics` was specified, filter out any + // generic functions from consideration as well. + if !matches!( + kind, + DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Generator + ) { + return None; + } else if ignore_unused_generics + && tcx.generics_of(def_id).requires_monomorphization(tcx) + { return None; } Some(local_def_id.to_def_id()) @@ -299,79 +318,17 @@ fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { let codegenned_def_ids = tcx.codegened_and_inlined_items(()); - let mut unused_def_ids_by_file: FxHashMap> = FxHashMap::default(); - for &non_codegenned_def_id in all_def_ids.difference(codegenned_def_ids) { - // Make sure the non-codegenned (unused) function has at least one MIR - // `Coverage` statement with a code region, and return its file name. - if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) { - let def_ids = - unused_def_ids_by_file.entry(*non_codegenned_file_name).or_insert_with(Vec::new); - def_ids.push(non_codegenned_def_id); - } - } + for &non_codegenned_def_id in eligible_def_ids.difference(codegenned_def_ids) { + let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id); - if unused_def_ids_by_file.is_empty() { - // There are no unused functions with file names to add (in any CGU) - return; - } - - // Each `CodegenUnit` (CGU) has its own function_coverage_map, and generates a specific binary - // with its own coverage map. - // - // Each covered function `Instance` can be included in only one coverage map, produced from a - // specific function_coverage_map, from a specific CGU. - // - // Since unused functions did not generate code, they are not associated with any CGU yet. - // - // To avoid injecting the unused functions in multiple coverage maps (for multiple CGUs) - // determine which function_coverage_map has the responsibility for publishing unreachable - // coverage, based on file name: For each unused function, find the CGU that generates the - // first function (based on sorted `DefId`) from the same file. - // - // Add a new `FunctionCoverage` to the `function_coverage_map`, with unreachable code regions - // for each region in it's MIR. - - // Convert the `HashSet` of `codegenned_def_ids` to a sortable vector, and sort them. - let mut sorted_codegenned_def_ids: Vec = codegenned_def_ids.iter().copied().collect(); - sorted_codegenned_def_ids.sort_unstable(); - - let mut first_covered_def_id_by_file: FxHashMap = FxHashMap::default(); - for &def_id in sorted_codegenned_def_ids.iter() { - if let Some(covered_file_name) = tcx.covered_file_name(def_id) { - // Only add files known to have unused functions - if unused_def_ids_by_file.contains_key(covered_file_name) { - first_covered_def_id_by_file.entry(*covered_file_name).or_insert(def_id); - } + // If a function is marked `#[no_coverage]`, then skip generating a + // dead code stub for it. + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) { + debug!("skipping unused fn marked #[no_coverage]: {:?}", non_codegenned_def_id); + continue; } - } - - // Get the set of def_ids with coverage regions, known by *this* CoverageContext. - let cgu_covered_def_ids: DefIdSet = match cx.coverage_context() { - Some(ctx) => ctx - .function_coverage_map - .borrow() - .keys() - .map(|&instance| instance.def.def_id()) - .collect(), - None => return, - }; - let cgu_covered_files: FxHashSet = first_covered_def_id_by_file - .iter() - .filter_map( - |(&file_name, def_id)| { - if cgu_covered_def_ids.contains(def_id) { Some(file_name) } else { None } - }, - ) - .collect(); - - // For each file for which this CGU is responsible for adding unused function coverage, - // get the `def_id`s for each unused function (if any), define a synthetic function with a - // single LLVM coverage counter, and add the function's coverage `CodeRegion`s. to the - // function_coverage_map. - for covered_file_name in cgu_covered_files { - for def_id in unused_def_ids_by_file.remove(&covered_file_name).into_iter().flatten() { - cx.define_unused_fn(def_id); - } + debug!("generating unused fn: {:?}", non_codegenned_def_id); + cx.define_unused_fn(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 60ff18af0a..1abc3fb523 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -10,6 +10,8 @@ use super::CrateDebugContext; use crate::abi; use crate::common::CodegenCx; +use crate::debuginfo::utils::fat_pointer_kind; +use crate::debuginfo::utils::FatPtrKind; use crate::llvm; use crate::llvm::debuginfo::{ DIArray, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, @@ -19,6 +21,7 @@ use crate::value::Value; use cstr::cstr; use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo; +use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -27,18 +30,18 @@ use rustc_fs_util::path_to_c_string; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::bug; use rustc_middle::mir::{self, GeneratorLayout}; use rustc_middle::ty::layout::{self, IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{ self, AdtKind, GeneratorSubsts, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES, }; -use rustc_middle::{bug, span_bug}; use rustc_query_system::ich::NodeIdHashingMode; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::Symbol; use rustc_span::FileNameDisplayPreference; -use rustc_span::{self, SourceFile, SourceFileHash, Span}; +use rustc_span::{self, SourceFile, SourceFileHash}; use rustc_target::abi::{Abi, Align, HasDataLayout, Integer, TagEncoding}; use rustc_target::abi::{Int, Pointer, F32, F64}; use rustc_target::abi::{Primitive, Size, VariantIdx, Variants}; @@ -182,9 +185,9 @@ impl<'ll, 'tcx> TypeMap<'ll, 'tcx> { /// /// This function is used to remove the temporary metadata /// mapping after we've computed the actual metadata. - fn remove_type(&mut self, type_: Ty<'tcx>) { - if self.type_to_metadata.remove(type_).is_none() { - bug!("type metadata `Ty` '{}' is not in the `TypeMap`!", type_); + fn remove_type(&mut self, ty: Ty<'tcx>) { + if self.type_to_metadata.remove(&ty).is_none() { + bug!("type metadata `Ty` '{}' is not in the `TypeMap`!", ty); } } @@ -274,6 +277,12 @@ impl<'ll, 'tcx> TypeMap<'ll, 'tcx> { ) -> String { format!("{}_variant_part", self.get_unique_type_id_as_string(enum_type_id)) } + + /// Gets the `UniqueTypeId` for the type of a vtable. + fn get_unique_type_id_of_vtable_type(&mut self, vtable_type_name: &str) -> UniqueTypeId { + let interner_key = self.unique_id_interner.intern(vtable_type_name); + interner_key + } } /// A description of some recursive type. It can either be already finished (as @@ -349,14 +358,15 @@ impl<'ll, 'tcx> RecursiveTypeDescription<'ll, 'tcx> { // ... then create the member descriptions ... let member_descriptions = member_description_factory.create_member_descriptions(cx); + let type_params = compute_type_parameters(cx, unfinished_type); // ... and attach them to the stub to complete it. set_members_of_composite_type( cx, - unfinished_type, member_holding_stub, member_descriptions, None, + type_params, ); MetadataCreationResult::new(metadata_stub, true) } @@ -376,23 +386,24 @@ macro_rules! return_if_metadata_created_in_meantime { }; } -fn fixed_vec_metadata<'ll, 'tcx>( +/// Creates debuginfo for a fixed size array (e.g. `[u64; 123]`). +/// For slices (that is, "arrays" of unknown size) use [slice_type_metadata]. +fn fixed_size_array_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId, - array_or_slice_type: Ty<'tcx>, - element_type: Ty<'tcx>, - span: Span, + array_type: Ty<'tcx>, ) -> MetadataCreationResult<'ll> { - let element_type_metadata = type_metadata(cx, element_type, span); + let ty::Array(element_type, len) = array_type.kind() else { + bug!("fixed_size_array_metadata() called with non-ty::Array type `{:?}`", array_type) + }; + + let element_type_metadata = type_metadata(cx, *element_type); return_if_metadata_created_in_meantime!(cx, unique_type_id); - let (size, align) = cx.size_and_align_of(array_or_slice_type); + let (size, align) = cx.size_and_align_of(array_type); - let upper_bound = match array_or_slice_type.kind() { - ty::Array(_, len) => len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong, - _ => -1, - }; + let upper_bound = len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong; let subrange = unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; @@ -411,67 +422,117 @@ fn fixed_vec_metadata<'ll, 'tcx>( MetadataCreationResult::new(metadata, false) } -fn vec_slice_metadata<'ll, 'tcx>( +/// Creates debuginfo for built-in pointer-like things: +/// +/// - ty::Ref +/// - ty::RawPtr +/// - ty::Adt in the case it's Box +/// +/// At some point we might want to remove the special handling of Box +/// and treat it the same as other smart pointers (like Rc, Arc, ...). +fn pointer_or_reference_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - slice_ptr_type: Ty<'tcx>, - element_type: Ty<'tcx>, + ptr_type: Ty<'tcx>, + pointee_type: Ty<'tcx>, unique_type_id: UniqueTypeId, - span: Span, ) -> MetadataCreationResult<'ll> { - let data_ptr_type = cx.tcx.mk_imm_ptr(element_type); - - let data_ptr_metadata = type_metadata(cx, data_ptr_type, span); + let pointee_type_metadata = type_metadata(cx, pointee_type); return_if_metadata_created_in_meantime!(cx, unique_type_id); - let slice_type_name = compute_debuginfo_type_name(cx.tcx, slice_ptr_type, true); - - let (pointer_size, pointer_align) = cx.size_and_align_of(data_ptr_type); - let (usize_size, usize_align) = cx.size_and_align_of(cx.tcx.types.usize); - - let member_descriptions = vec![ - MemberDescription { - name: "data_ptr".to_owned(), - type_metadata: data_ptr_metadata, - offset: Size::ZERO, - size: pointer_size, - align: pointer_align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - }, - MemberDescription { - name: "length".to_owned(), - type_metadata: type_metadata(cx, cx.tcx.types.usize, span), - offset: pointer_size, - size: usize_size, - align: usize_align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - }, - ]; + let (thin_pointer_size, thin_pointer_align) = + cx.size_and_align_of(cx.tcx.mk_imm_ptr(cx.tcx.types.unit)); + let ptr_type_debuginfo_name = compute_debuginfo_type_name(cx.tcx, ptr_type, true); - let file_metadata = unknown_file_metadata(cx); + let pointer_type_metadata = match fat_pointer_kind(cx, pointee_type) { + None => { + // This is a thin pointer. Create a regular pointer type and give it the correct name. + debug_assert_eq!( + (thin_pointer_size, thin_pointer_align), + cx.size_and_align_of(ptr_type) + ); - let metadata = composite_type_metadata( - cx, - slice_ptr_type, - &slice_type_name, - unique_type_id, - member_descriptions, - NO_SCOPE_METADATA, - file_metadata, - span, - ); - MetadataCreationResult::new(metadata, false) + unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_metadata, + thin_pointer_size.bits(), + thin_pointer_align.bits() as u32, + 0, // Ignore DWARF address space. + ptr_type_debuginfo_name.as_ptr().cast(), + ptr_type_debuginfo_name.len(), + ) + } + } + Some(fat_pointer_kind) => { + let layout = cx.layout_of(ptr_type); + + let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); + let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); + + let (addr_field_name, extra_field_name) = match fat_pointer_kind { + FatPtrKind::Dyn => ("pointer", "vtable"), + FatPtrKind::Slice => ("data_ptr", "length"), + }; + + debug_assert_eq!(abi::FAT_PTR_ADDR, 0); + debug_assert_eq!(abi::FAT_PTR_EXTRA, 1); + + // The data pointer type is a regular, thin pointer, regardless of whether this is a slice + // or a trait object. + let data_ptr_type_metadata = unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_metadata, + addr_field.size.bits(), + addr_field.align.abi.bits() as u32, + 0, // Ignore DWARF address space. + std::ptr::null(), + 0, + ) + }; + + let member_descriptions = vec![ + MemberDescription { + name: addr_field_name.into(), + type_metadata: data_ptr_type_metadata, + offset: layout.fields.offset(abi::FAT_PTR_ADDR), + size: addr_field.size, + align: addr_field.align.abi, + flags: DIFlags::FlagZero, + discriminant: None, + source_info: None, + }, + MemberDescription { + name: extra_field_name.into(), + type_metadata: type_metadata(cx, extra_field.ty), + offset: layout.fields.offset(abi::FAT_PTR_EXTRA), + size: extra_field.size, + align: extra_field.align.abi, + flags: DIFlags::FlagZero, + discriminant: None, + source_info: None, + }, + ]; + + composite_type_metadata( + cx, + ptr_type, + &ptr_type_debuginfo_name, + unique_type_id, + member_descriptions, + NO_SCOPE_METADATA, + ) + } + }; + + MetadataCreationResult { metadata: pointer_type_metadata, already_stored_in_typemap: false } } fn subroutine_type_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId, signature: ty::PolyFnSig<'tcx>, - span: Span, ) -> MetadataCreationResult<'ll> { let signature = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), signature); @@ -480,12 +541,12 @@ fn subroutine_type_metadata<'ll, 'tcx>( // return type match signature.output().kind() { ty::Tuple(tys) if tys.is_empty() => None, - _ => Some(type_metadata(cx, signature.output(), span)), + _ => Some(type_metadata(cx, signature.output())), }, ) .chain( // regular arguments - signature.inputs().iter().map(|argument_type| Some(type_metadata(cx, argument_type, span))), + signature.inputs().iter().map(|&argument_type| Some(type_metadata(cx, argument_type))), ) .collect(); @@ -502,98 +563,60 @@ fn subroutine_type_metadata<'ll, 'tcx>( ) } -// FIXME(1563): This is all a bit of a hack because 'trait pointer' is an ill- -// defined concept. For the case of an actual trait pointer (i.e., `Box`, -// `&Trait`), `trait_object_type` should be the whole thing (e.g, `Box`) and -// `trait_type` should be the actual trait (e.g., `Trait`). Where the trait is part -// of a DST struct, there is no `trait_object_type` and the results of this -// function will be a little bit weird. -fn trait_pointer_metadata<'ll, 'tcx>( +/// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs +/// we with the correct type name (e.g. "dyn SomeTrait + Sync"). +fn dyn_type_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - trait_type: Ty<'tcx>, - trait_object_type: Option>, + dyn_type: Ty<'tcx>, unique_type_id: UniqueTypeId, ) -> &'ll DIType { - // The implementation provided here is a stub. It makes sure that the trait - // type is assigned the correct name, size, namespace, and source location. - // However, it does not describe the trait's methods. - - let (containing_scope, trait_type_name) = match trait_object_type { - Some(trait_object_type) => match trait_object_type.kind() { - ty::Adt(def, _) => ( - Some(get_namespace_for_item(cx, def.did)), - compute_debuginfo_type_name(cx.tcx, trait_object_type, false), - ), - ty::RawPtr(_) | ty::Ref(..) => { - (NO_SCOPE_METADATA, compute_debuginfo_type_name(cx.tcx, trait_object_type, true)) - } - _ => { - bug!( - "debuginfo: unexpected trait-object type in \ - trait_pointer_metadata(): {:?}", - trait_object_type - ); - } - }, + if let ty::Dynamic(..) = dyn_type.kind() { + let type_name = compute_debuginfo_type_name(cx.tcx, dyn_type, true); + composite_type_metadata(cx, dyn_type, &type_name, unique_type_id, vec![], NO_SCOPE_METADATA) + } else { + bug!("Only ty::Dynamic is valid for dyn_type_metadata(). Found {:?} instead.", dyn_type) + } +} - // No object type, use the trait type directly (no scope here since the type - // will be wrapped in the dyn$ synthetic type). - None => (NO_SCOPE_METADATA, compute_debuginfo_type_name(cx.tcx, trait_type, true)), +/// Create debuginfo for `[T]` and `str`. These are unsized. +/// +/// NOTE: We currently emit just emit the debuginfo for the element type here +/// (i.e. `T` for slices and `u8` for `str`), so that we end up with +/// `*const T` for the `data_ptr` field of the corresponding fat-pointer +/// debuginfo of `&[T]`. +/// +/// It would be preferable and more accurate if we emitted a DIArray of T +/// without an upper bound instead. That is, LLVM already supports emitting +/// debuginfo of arrays of unknown size. But GDB currently seems to end up +/// in an infinite loop when confronted with such a type. +/// +/// As a side effect of the current encoding every instance of a type like +/// `struct Foo { unsized_field: [u8] }` will look like +/// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the +/// slice is zero, then accessing `unsized_field` in the debugger would +/// result in an out-of-bounds access. +fn slice_type_metadata<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + slice_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, +) -> MetadataCreationResult<'ll> { + let element_type = match slice_type.kind() { + ty::Slice(element_type) => *element_type, + ty::Str => cx.tcx.types.u8, + _ => { + bug!( + "Only ty::Slice is valid for slice_type_metadata(). Found {:?} instead.", + slice_type + ) + } }; - let file_metadata = unknown_file_metadata(cx); - - let layout = cx.layout_of(cx.tcx.mk_mut_ptr(trait_type)); - - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - - let data_ptr_field = layout.field(cx, 0); - let vtable_field = layout.field(cx, 1); - let member_descriptions = vec![ - MemberDescription { - name: "pointer".to_owned(), - type_metadata: type_metadata( - cx, - cx.tcx.mk_mut_ptr(cx.tcx.types.u8), - rustc_span::DUMMY_SP, - ), - offset: layout.fields.offset(0), - size: data_ptr_field.size, - align: data_ptr_field.align.abi, - flags: DIFlags::FlagArtificial, - discriminant: None, - source_info: None, - }, - MemberDescription { - name: "vtable".to_owned(), - type_metadata: type_metadata(cx, vtable_field.ty, rustc_span::DUMMY_SP), - offset: layout.fields.offset(1), - size: vtable_field.size, - align: vtable_field.align.abi, - flags: DIFlags::FlagArtificial, - discriminant: None, - source_info: None, - }, - ]; - - composite_type_metadata( - cx, - trait_object_type.unwrap_or(trait_type), - &trait_type_name, - unique_type_id, - member_descriptions, - containing_scope, - file_metadata, - rustc_span::DUMMY_SP, - ) + let element_type_metadata = type_metadata(cx, element_type); + return_if_metadata_created_in_meantime!(cx, unique_type_id); + MetadataCreationResult { metadata: element_type_metadata, already_stored_in_typemap: false } } -pub fn type_metadata<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - t: Ty<'tcx>, - usage_site_span: Span, -) -> &'ll DIType { +pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { // Get the unique type ID of this type. let unique_type_id = { let mut type_map = debug_context(cx).type_map.borrow_mut(); @@ -629,26 +652,6 @@ pub fn type_metadata<'ll, 'tcx>( debug!("type_metadata: {:?}", t); - let ptr_metadata = |ty: Ty<'tcx>| match *ty.kind() { - ty::Slice(typ) => Ok(vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span)), - ty::Str => Ok(vec_slice_metadata(cx, t, cx.tcx.types.u8, unique_type_id, usage_site_span)), - ty::Dynamic(..) => Ok(MetadataCreationResult::new( - trait_pointer_metadata(cx, ty, Some(t), unique_type_id), - false, - )), - _ => { - let pointee_metadata = type_metadata(cx, ty, usage_site_span); - - if let Some(metadata) = - debug_context(cx).type_map.borrow().find_metadata_for_unique_id(unique_type_id) - { - return Err(metadata); - } - - Ok(MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), false)) - } - }; - let MetadataCreationResult { metadata, already_stored_in_typemap } = match *t.kind() { ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { MetadataCreationResult::new(basic_type_metadata(cx, t), false) @@ -656,24 +659,20 @@ pub fn type_metadata<'ll, 'tcx>( ty::Tuple(elements) if elements.is_empty() => { MetadataCreationResult::new(basic_type_metadata(cx, t), false) } - ty::Array(typ, _) | ty::Slice(typ) => { - fixed_vec_metadata(cx, unique_type_id, t, typ, usage_site_span) - } - ty::Str => fixed_vec_metadata(cx, unique_type_id, t, cx.tcx.types.i8, usage_site_span), + ty::Array(..) => fixed_size_array_metadata(cx, unique_type_id, t), + ty::Slice(_) | ty::Str => slice_type_metadata(cx, t, unique_type_id), ty::Dynamic(..) => { - MetadataCreationResult::new(trait_pointer_metadata(cx, t, None, unique_type_id), false) + MetadataCreationResult::new(dyn_type_metadata(cx, t, unique_type_id), false) } ty::Foreign(..) => { MetadataCreationResult::new(foreign_type_metadata(cx, t, unique_type_id), false) } - ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => match ptr_metadata(ty) { - Ok(res) => res, - Err(metadata) => return metadata, - }, - ty::Adt(def, _) if def.is_box() => match ptr_metadata(t.boxed_ty()) { - Ok(res) => res, - Err(metadata) => return metadata, - }, + ty::RawPtr(ty::TypeAndMut { ty: pointee_type, .. }) | ty::Ref(_, pointee_type, _) => { + pointer_or_reference_metadata(cx, t, pointee_type, unique_type_id) + } + ty::Adt(def, _) if def.is_box() => { + pointer_or_reference_metadata(cx, t, t.boxed_ty(), unique_type_id) + } ty::FnDef(..) | ty::FnPtr(_) => { if let Some(metadata) = debug_context(cx).type_map.borrow().find_metadata_for_unique_id(unique_type_id) @@ -710,26 +709,33 @@ pub fn type_metadata<'ll, 'tcx>( type_map.borrow_mut().register_type_with_metadata(t, temp_type); let fn_metadata = - subroutine_type_metadata(cx, unique_type_id, t.fn_sig(cx.tcx), usage_site_span) - .metadata; + subroutine_type_metadata(cx, unique_type_id, t.fn_sig(cx.tcx)).metadata; type_map.borrow_mut().remove_type(t); // This is actually a function pointer, so wrap it in pointer DI. - MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) + let (pointer_size, pointer_align) = + cx.size_and_align_of(cx.tcx.mk_imm_ptr(cx.tcx.mk_unit())); + let name = compute_debuginfo_type_name(cx.tcx, t, false); + let md = unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + fn_metadata, + pointer_size.bits(), + pointer_align.bits() as u32, + 0, // Ignore DWARF address space. + name.as_ptr().cast(), + name.len(), + ) + }; + + MetadataCreationResult::new(md, false) } ty::Closure(def_id, substs) => { let upvar_tys: Vec<_> = substs.as_closure().upvar_tys().collect(); let containing_scope = get_namespace_for_item(cx, def_id); - prepare_tuple_metadata( - cx, - t, - &upvar_tys, - unique_type_id, - usage_site_span, - Some(containing_scope), - ) - .finalize(cx) + prepare_tuple_metadata(cx, t, &upvar_tys, unique_type_id, Some(containing_scope)) + .finalize(cx) } ty::Generator(def_id, substs, _) => { let upvar_tys: Vec<_> = substs @@ -737,25 +743,18 @@ pub fn type_metadata<'ll, 'tcx>( .prefix_tys() .map(|t| cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)) .collect(); - prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span, upvar_tys) - .finalize(cx) + prepare_enum_metadata(cx, t, def_id, unique_type_id, upvar_tys).finalize(cx) } ty::Adt(def, ..) => match def.adt_kind() { - AdtKind::Struct => { - prepare_struct_metadata(cx, t, unique_type_id, usage_site_span).finalize(cx) - } - AdtKind::Union => { - prepare_union_metadata(cx, t, unique_type_id, usage_site_span).finalize(cx) - } + AdtKind::Struct => prepare_struct_metadata(cx, t, unique_type_id).finalize(cx), + AdtKind::Union => prepare_union_metadata(cx, t, unique_type_id).finalize(cx), AdtKind::Enum => { - prepare_enum_metadata(cx, t, def.did, unique_type_id, usage_site_span, vec![]) - .finalize(cx) + prepare_enum_metadata(cx, t, def.did, unique_type_id, vec![]).finalize(cx) } }, ty::Tuple(elements) => { let tys: Vec<_> = elements.iter().map(|k| k.expect_ty()).collect(); - prepare_tuple_metadata(cx, t, &tys, unique_type_id, usage_site_span, NO_SCOPE_METADATA) - .finalize(cx) + prepare_tuple_metadata(cx, t, &tys, unique_type_id, NO_SCOPE_METADATA).finalize(cx) } // Type parameters from polymorphized functions. ty::Param(_) => MetadataCreationResult::new(param_type_metadata(cx, t), false), @@ -770,8 +769,7 @@ pub fn type_metadata<'ll, 'tcx>( let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { Some(metadata) => metadata, None => { - span_bug!( - usage_site_span, + bug!( "expected type metadata for unique \ type ID '{}' to already be in \ the `debuginfo::TypeMap` but it \ @@ -785,8 +783,7 @@ pub fn type_metadata<'ll, 'tcx>( match type_map.find_metadata_for_type(t) { Some(metadata) => { if metadata != metadata_for_uid { - span_bug!( - usage_site_span, + bug!( "mismatch between `Ty` and \ `UniqueTypeId` maps in \ `debuginfo::TypeMap`. \ @@ -994,27 +991,17 @@ fn foreign_type_metadata<'ll, 'tcx>( debug!("foreign_type_metadata: {:?}", t); let name = compute_debuginfo_type_name(cx.tcx, t, false); - create_struct_stub(cx, t, &name, unique_type_id, NO_SCOPE_METADATA, DIFlags::FlagZero) -} - -fn pointer_type_metadata<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - pointer_type: Ty<'tcx>, - pointee_type_metadata: &'ll DIType, -) -> &'ll DIType { - let (pointer_size, pointer_align) = cx.size_and_align_of(pointer_type); - let name = compute_debuginfo_type_name(cx.tcx, pointer_type, false); - unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_metadata, - pointer_size.bits(), - pointer_align.bits() as u32, - 0, // Ignore DWARF address space. - name.as_ptr().cast(), - name.len(), - ) - } + let (size, align) = cx.size_and_align_of(t); + create_struct_stub( + cx, + size, + align, + &name, + unique_type_id, + NO_SCOPE_METADATA, + DIFlags::FlagZero, + None, + ) } fn param_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { @@ -1283,7 +1270,6 @@ impl<'ll, 'tcx> MemberDescriptionFactory<'ll, 'tcx> { struct StructMemberDescriptionFactory<'tcx> { ty: Ty<'tcx>, variant: &'tcx ty::VariantDef, - span: Span, } impl<'tcx> StructMemberDescriptionFactory<'tcx> { @@ -1300,12 +1286,12 @@ impl<'tcx> StructMemberDescriptionFactory<'tcx> { let name = if self.variant.ctor_kind == CtorKind::Fn { format!("__{}", i) } else { - f.ident.to_string() + f.name.to_string() }; let field = layout.field(cx, i); MemberDescription { name, - type_metadata: type_metadata(cx, field.ty, self.span), + type_metadata: type_metadata(cx, field.ty), offset: layout.fields.offset(i), size: field.size, align: field.align.abi, @@ -1322,7 +1308,6 @@ fn prepare_struct_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, struct_type: Ty<'tcx>, unique_type_id: UniqueTypeId, - span: Span, ) -> RecursiveTypeDescription<'ll, 'tcx> { let struct_name = compute_debuginfo_type_name(cx.tcx, struct_type, false); @@ -1332,14 +1317,17 @@ fn prepare_struct_metadata<'ll, 'tcx>( }; let containing_scope = get_namespace_for_item(cx, struct_def_id); + let (size, align) = cx.size_and_align_of(struct_type); let struct_metadata_stub = create_struct_stub( cx, - struct_type, + size, + align, &struct_name, unique_type_id, Some(containing_scope), DIFlags::FlagZero, + None, ); create_and_register_recursive_type_forward_declaration( @@ -1348,7 +1336,7 @@ fn prepare_struct_metadata<'ll, 'tcx>( unique_type_id, struct_metadata_stub, struct_metadata_stub, - StructMDF(StructMemberDescriptionFactory { ty: struct_type, variant, span }), + StructMDF(StructMemberDescriptionFactory { ty: struct_type, variant }), ) } @@ -1385,7 +1373,6 @@ fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> struct TupleMemberDescriptionFactory<'tcx> { ty: Ty<'tcx>, component_types: Vec>, - span: Span, } impl<'tcx> TupleMemberDescriptionFactory<'tcx> { @@ -1412,7 +1399,7 @@ impl<'tcx> TupleMemberDescriptionFactory<'tcx> { }; MemberDescription { name, - type_metadata: type_metadata(cx, component_type, self.span), + type_metadata: type_metadata(cx, component_type), offset: layout.fields.offset(i), size, align, @@ -1430,18 +1417,20 @@ fn prepare_tuple_metadata<'ll, 'tcx>( tuple_type: Ty<'tcx>, component_types: &[Ty<'tcx>], unique_type_id: UniqueTypeId, - span: Span, containing_scope: Option<&'ll DIScope>, ) -> RecursiveTypeDescription<'ll, 'tcx> { + let (size, align) = cx.size_and_align_of(tuple_type); let tuple_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false); let struct_stub = create_struct_stub( cx, - tuple_type, + size, + align, &tuple_name[..], unique_type_id, containing_scope, DIFlags::FlagZero, + None, ); create_and_register_recursive_type_forward_declaration( @@ -1453,7 +1442,6 @@ fn prepare_tuple_metadata<'ll, 'tcx>( TupleMDF(TupleMemberDescriptionFactory { ty: tuple_type, component_types: component_types.to_vec(), - span, }), ) } @@ -1465,7 +1453,6 @@ fn prepare_tuple_metadata<'ll, 'tcx>( struct UnionMemberDescriptionFactory<'tcx> { layout: TyAndLayout<'tcx>, variant: &'tcx ty::VariantDef, - span: Span, } impl<'tcx> UnionMemberDescriptionFactory<'tcx> { @@ -1480,8 +1467,8 @@ impl<'tcx> UnionMemberDescriptionFactory<'tcx> { .map(|(i, f)| { let field = self.layout.field(cx, i); MemberDescription { - name: f.ident.to_string(), - type_metadata: type_metadata(cx, field.ty, self.span), + name: f.name.to_string(), + type_metadata: type_metadata(cx, field.ty), offset: Size::ZERO, size: field.size, align: field.align.abi, @@ -1498,7 +1485,6 @@ fn prepare_union_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, union_type: Ty<'tcx>, unique_type_id: UniqueTypeId, - span: Span, ) -> RecursiveTypeDescription<'ll, 'tcx> { let union_name = compute_debuginfo_type_name(cx.tcx, union_type, false); @@ -1518,7 +1504,7 @@ fn prepare_union_metadata<'ll, 'tcx>( unique_type_id, union_metadata_stub, union_metadata_stub, - UnionMDF(UnionMemberDescriptionFactory { layout: cx.layout_of(union_type), variant, span }), + UnionMDF(UnionMemberDescriptionFactory { layout: cx.layout_of(union_type), variant }), ) } @@ -1538,7 +1524,7 @@ fn generator_layout_and_saved_local_names<'tcx>( let state_arg = mir::Local::new(1); for var in &body.var_debug_info { - let place = if let mir::VarDebugInfoContents::Place(p) = var.value { p } else { continue }; + let mir::VarDebugInfoContents::Place(place) = &var.value else { continue }; if place.local != state_arg { continue; } @@ -1573,7 +1559,6 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> { layout: TyAndLayout<'tcx>, tag_type_metadata: Option<&'ll DIType>, common_members: Vec>, - span: Span, } impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { @@ -1605,7 +1590,7 @@ impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { // msvc, then we need to use a different, fallback encoding of the debuginfo. let fallback = cpp_like_debuginfo(cx.tcx); // This will always find the metadata in the type map. - let self_metadata = type_metadata(cx, self.enum_type, self.span); + let self_metadata = type_metadata(cx, self.enum_type); match self.layout.variants { Variants::Single { index } => { @@ -1617,16 +1602,17 @@ impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { let variant_info = variant_info_for(index); let (variant_type_metadata, member_description_factory) = - describe_enum_variant(cx, self.layout, variant_info, self_metadata, self.span); + describe_enum_variant(cx, self.layout, variant_info, self_metadata); let member_descriptions = member_description_factory.create_member_descriptions(cx); + let type_params = compute_type_parameters(cx, self.enum_type); set_members_of_composite_type( cx, - self.enum_type, variant_type_metadata, member_descriptions, Some(&self.common_members), + type_params, ); vec![MemberDescription { name: variant_info.variant_name(), @@ -1682,23 +1668,19 @@ impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { .map(|(i, _)| { let variant = self.layout.for_variant(cx, i); let variant_info = variant_info_for(i); - let (variant_type_metadata, member_desc_factory) = describe_enum_variant( - cx, - variant, - variant_info, - self_metadata, - self.span, - ); + let (variant_type_metadata, member_desc_factory) = + describe_enum_variant(cx, variant, variant_info, self_metadata); let member_descriptions = member_desc_factory.create_member_descriptions(cx); + let type_params = compute_type_parameters(cx, self.enum_type); set_members_of_composite_type( cx, - self.enum_type, variant_type_metadata, member_descriptions, Some(&self.common_members), + type_params, ); MemberDescription { @@ -1807,7 +1789,7 @@ impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { tag.value.size(cx).bits(), tag.value.align(cx).abi.bits() as u32, create_DIArray(DIB(cx), &tags), - type_metadata(cx, discr_enum_ty, self.span), + type_metadata(cx, discr_enum_ty), true, ) }; @@ -1818,17 +1800,17 @@ impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { dataful_variant_layout, variant_info, self_metadata, - self.span, ); let member_descriptions = member_desc_factory.create_member_descriptions(cx); + let type_params = compute_type_parameters(cx, self.enum_type); set_members_of_composite_type( cx, - self.enum_type, variant_type_metadata, member_descriptions, Some(&self.common_members), + type_params, ); let (size, align) = @@ -1864,23 +1846,18 @@ impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { let variant = self.layout.for_variant(cx, i); let variant_info = variant_info_for(i); let (variant_type_metadata, member_desc_factory) = - describe_enum_variant( - cx, - variant, - variant_info, - self_metadata, - self.span, - ); + describe_enum_variant(cx, variant, variant_info, self_metadata); let member_descriptions = member_desc_factory.create_member_descriptions(cx); + let type_params = compute_type_parameters(cx, self.enum_type); set_members_of_composite_type( cx, - self.enum_type, variant_type_metadata, member_descriptions, Some(&self.common_members), + type_params, ); let niche_value = calculate_niche_value(i); @@ -1908,7 +1885,6 @@ struct VariantMemberDescriptionFactory<'tcx> { /// Cloned from the `layout::Struct` describing the variant. offsets: Vec, args: Vec<(String, Ty<'tcx>)>, - span: Span, } impl<'tcx> VariantMemberDescriptionFactory<'tcx> { @@ -1923,7 +1899,7 @@ impl<'tcx> VariantMemberDescriptionFactory<'tcx> { let (size, align) = cx.size_and_align_of(ty); MemberDescription { name: name.to_string(), - type_metadata: type_metadata(cx, ty, self.span), + type_metadata: type_metadata(cx, ty), offset: self.offsets[i], size, align, @@ -1950,7 +1926,7 @@ enum VariantInfo<'a, 'tcx> { impl<'tcx> VariantInfo<'_, 'tcx> { fn map_struct_name(&self, f: impl FnOnce(&str) -> R) -> R { match self { - VariantInfo::Adt(variant) => f(variant.ident.as_str()), + VariantInfo::Adt(variant) => f(variant.name.as_str()), VariantInfo::Generator { variant_index, .. } => { f(&GeneratorSubsts::variant_name(*variant_index)) } @@ -1959,7 +1935,7 @@ impl<'tcx> VariantInfo<'_, 'tcx> { fn variant_name(&self) -> String { match self { - VariantInfo::Adt(variant) => variant.ident.to_string(), + VariantInfo::Adt(variant) => variant.name.to_string(), VariantInfo::Generator { variant_index, .. } => { // Since GDB currently prints out the raw discriminant along // with every variant, make each variant name be just the value @@ -1973,7 +1949,7 @@ impl<'tcx> VariantInfo<'_, 'tcx> { fn field_name(&self, i: usize) -> String { let field_name = match *self { VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn => { - Some(variant.fields[i].ident.name) + Some(variant.fields[i].name) } VariantInfo::Generator { generator_layout, @@ -2011,20 +1987,24 @@ fn describe_enum_variant<'ll, 'tcx>( layout: layout::TyAndLayout<'tcx>, variant: VariantInfo<'_, 'tcx>, containing_scope: &'ll DIScope, - span: Span, ) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) { let metadata_stub = variant.map_struct_name(|variant_name| { let unique_type_id = debug_context(cx) .type_map .borrow_mut() .get_unique_type_id_of_enum_variant(cx, layout.ty, variant_name); + + let (size, align) = cx.size_and_align_of(layout.ty); + create_struct_stub( cx, - layout.ty, + size, + align, variant_name, unique_type_id, Some(containing_scope), DIFlags::FlagZero, + None, ) }); @@ -2033,8 +2013,7 @@ fn describe_enum_variant<'ll, 'tcx>( .map(|i| (variant.field_name(i), layout.field(cx, i).ty)) .collect(); - let member_description_factory = - VariantMDF(VariantMemberDescriptionFactory { offsets, args, span }); + let member_description_factory = VariantMDF(VariantMemberDescriptionFactory { offsets, args }); (metadata_stub, member_description_factory) } @@ -2044,7 +2023,6 @@ fn prepare_enum_metadata<'ll, 'tcx>( enum_type: Ty<'tcx>, enum_def_id: DefId, unique_type_id: UniqueTypeId, - span: Span, outer_field_tys: Vec>, ) -> RecursiveTypeDescription<'ll, 'tcx> { let tcx = cx.tcx; @@ -2063,7 +2041,7 @@ fn prepare_enum_metadata<'ll, 'tcx>( let enumerators_metadata: Vec<_> = match enum_type.kind() { ty::Adt(def, _) => iter::zip(def.discriminants(tcx), &def.variants) .map(|((_, discr), v)| { - let name = v.ident.as_str(); + let name = v.name.as_str(); let is_unsigned = match discr.ty.kind() { ty::Int(_) => false, ty::Uint(_) => true, @@ -2109,8 +2087,7 @@ fn prepare_enum_metadata<'ll, 'tcx>( Some(discriminant_type_metadata) => discriminant_type_metadata, None => { let (discriminant_size, discriminant_align) = (discr.size(cx), discr.align(cx)); - let discriminant_base_type_metadata = - type_metadata(cx, discr.to_ty(tcx), rustc_span::DUMMY_SP); + let discriminant_base_type_metadata = type_metadata(cx, discr.to_ty(tcx)); let item_name; let discriminant_name = match enum_type.kind() { @@ -2202,7 +2179,6 @@ fn prepare_enum_metadata<'ll, 'tcx>( layout, tag_type_metadata: discriminant_type_metadata, common_members: vec![], - span, }), ); } @@ -2272,11 +2248,8 @@ fn prepare_enum_metadata<'ll, 'tcx>( let outer_fields = match layout.variants { Variants::Single { .. } => vec![], Variants::Multiple { .. } => { - let tuple_mdf = TupleMemberDescriptionFactory { - ty: enum_type, - component_types: outer_field_tys, - span, - }; + let tuple_mdf = + TupleMemberDescriptionFactory { ty: enum_type, component_types: outer_field_tys }; tuple_mdf .create_member_descriptions(cx) .into_iter() @@ -2352,7 +2325,6 @@ fn prepare_enum_metadata<'ll, 'tcx>( layout, tag_type_metadata: None, common_members: outer_fields, - span, }), ) } @@ -2368,28 +2340,28 @@ fn composite_type_metadata<'ll, 'tcx>( composite_type_unique_id: UniqueTypeId, member_descriptions: Vec>, containing_scope: Option<&'ll DIScope>, - - // Ignore source location information as long as it - // can't be reconstructed for non-local crates. - _file_metadata: &'ll DIFile, - _definition_span: Span, ) -> &'ll DICompositeType { + let (size, align) = cx.size_and_align_of(composite_type); + // Create the (empty) struct metadata node ... let composite_type_metadata = create_struct_stub( cx, - composite_type, + size, + align, composite_type_name, composite_type_unique_id, containing_scope, DIFlags::FlagZero, + None, ); + // ... and immediately create and add the member descriptions. set_members_of_composite_type( cx, - composite_type, composite_type_metadata, member_descriptions, None, + compute_type_parameters(cx, composite_type), ); composite_type_metadata @@ -2397,10 +2369,10 @@ fn composite_type_metadata<'ll, 'tcx>( fn set_members_of_composite_type<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - composite_type: Ty<'tcx>, composite_type_metadata: &'ll DICompositeType, member_descriptions: Vec>, common_members: Option<&Vec>>, + type_params: &'ll DIArray, ) { // In some rare cases LLVM metadata uniquing would lead to an existing type // description being used instead of a new one created in @@ -2427,13 +2399,12 @@ fn set_members_of_composite_type<'ll, 'tcx>( member_metadata.extend(other_members.iter()); } - let type_params = compute_type_parameters(cx, composite_type); unsafe { - let type_array = create_DIArray(DIB(cx), &member_metadata); + let field_array = create_DIArray(DIB(cx), &member_metadata); llvm::LLVMRustDICompositeTypeReplaceArrays( DIB(cx), composite_type_metadata, - Some(type_array), + Some(field_array), Some(type_params), ); } @@ -2450,8 +2421,7 @@ fn compute_type_parameters<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) - if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - let actual_type_metadata = - type_metadata(cx, actual_type, rustc_span::DUMMY_SP); + let actual_type_metadata = type_metadata(cx, actual_type); let name = name.as_str(); Some(unsafe { Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( @@ -2487,14 +2457,14 @@ fn compute_type_parameters<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) - /// with `set_members_of_composite_type()`. fn create_struct_stub<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - struct_type: Ty<'tcx>, - struct_type_name: &str, + size: Size, + align: Align, + type_name: &str, unique_type_id: UniqueTypeId, containing_scope: Option<&'ll DIScope>, flags: DIFlags, + vtable_holder: Option<&'ll DIType>, ) -> &'ll DICompositeType { - let (struct_size, struct_align) = cx.size_and_align_of(struct_type); - let type_map = debug_context(cx).type_map.borrow(); let unique_type_id = type_map.get_unique_type_id_as_string(unique_type_id); @@ -2507,17 +2477,17 @@ fn create_struct_stub<'ll, 'tcx>( llvm::LLVMRustDIBuilderCreateStructType( DIB(cx), containing_scope, - struct_type_name.as_ptr().cast(), - struct_type_name.len(), + type_name.as_ptr().cast(), + type_name.len(), unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER, - struct_size.bits(), - struct_align.bits() as u32, + size.bits(), + align.bits() as u32, flags, None, empty_array, 0, - None, + vtable_holder, unique_type_id.as_ptr().cast(), unique_type_id.len(), ) @@ -2593,7 +2563,7 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g let is_local_to_unit = is_node_local_to_unit(cx, def_id); let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all()); - let type_metadata = type_metadata(cx, variable_type, span); + let type_metadata = type_metadata(cx, variable_type); let var_name = tcx.item_name(def_id); let var_name = var_name.as_str(); let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)).name; @@ -2623,6 +2593,14 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g } /// Generates LLVM debuginfo for a vtable. +/// +/// The vtable type looks like a struct with a field for each function pointer and super-trait +/// pointer it contains (plus the `size` and `align` fields). +/// +/// Except for `size`, `align`, and `drop_in_place`, the field names don't try to mirror +/// the name of the method they implement. This can be implemented in the future once there +/// is a proper disambiguation scheme for dealing with methods from different traits that have +/// the same name. fn vtable_type_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, @@ -2639,16 +2617,81 @@ fn vtable_type_metadata<'ll, 'tcx>( COMMON_VTABLE_ENTRIES }; - // FIXME: We describe the vtable as an array of *const () pointers. The length of the array is - // correct - but we could create a more accurate description, e.g. by describing it - // as a struct where each field has a name that corresponds to the name of the method - // it points to. - // However, this is not entirely straightforward because there might be multiple - // methods with the same name if the vtable is for multiple traits. So for now we keep - // things simple instead of adding some ad-hoc disambiguation scheme. - let vtable_type = tcx.mk_array(tcx.mk_imm_ptr(tcx.types.unit), vtable_entries.len() as u64); + // All function pointers are described as opaque pointers. This could be improved in the future + // by describing them as actual function pointers. + let void_pointer_ty = tcx.mk_imm_ptr(tcx.types.unit); + let void_pointer_type_debuginfo = type_metadata(cx, void_pointer_ty); + let usize_debuginfo = type_metadata(cx, tcx.types.usize); + let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty); + // If `usize` is not pointer-sized and -aligned then the size and alignment computations + // for the vtable as a whole would be wrong. Let's make sure this holds even on weird + // platforms. + assert_eq!(cx.size_and_align_of(tcx.types.usize), (pointer_size, pointer_align)); + + let vtable_type_name = + compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::Type); + let unique_type_id = debug_context(cx) + .type_map + .borrow_mut() + .get_unique_type_id_of_vtable_type(&vtable_type_name); + let size = pointer_size * vtable_entries.len() as u64; + + // This gets mapped to a DW_AT_containing_type attribute which allows GDB to correlate + // the vtable to the type it is for. + let vtable_holder = type_metadata(cx, ty); + + let vtable_type_metadata = create_struct_stub( + cx, + size, + pointer_align, + &vtable_type_name, + unique_type_id, + NO_SCOPE_METADATA, + DIFlags::FlagArtificial, + Some(vtable_holder), + ); + + // Create a field for each entry in the vtable. + let fields: Vec<_> = vtable_entries + .iter() + .enumerate() + .filter_map(|(index, vtable_entry)| { + let (field_name, field_type) = match vtable_entry { + ty::VtblEntry::MetadataDropInPlace => { + ("drop_in_place".to_string(), void_pointer_type_debuginfo) + } + ty::VtblEntry::Method(_) => { + // Note: This code does not try to give a proper name to each method + // because there might be multiple methods with the same name + // (coming from different traits). + (format!("__method{}", index), void_pointer_type_debuginfo) + } + ty::VtblEntry::TraitVPtr(_) => { + // Note: In the future we could try to set the type of this pointer + // to the type that we generate for the corresponding vtable. + (format!("__super_trait_ptr{}", index), void_pointer_type_debuginfo) + } + ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_debuginfo), + ty::VtblEntry::MetadataSize => ("size".to_string(), usize_debuginfo), + ty::VtblEntry::Vacant => return None, + }; + + Some(MemberDescription { + name: field_name, + type_metadata: field_type, + offset: pointer_size * index as u64, + size: pointer_size, + align: pointer_align, + flags: DIFlags::FlagZero, + discriminant: None, + source_info: None, + }) + }) + .collect(); - type_metadata(cx, vtable_type, rustc_span::DUMMY_SP) + let type_params = create_DIArray(DIB(cx), &[]); + set_members_of_composite_type(cx, vtable_type_metadata, fields, None, type_params); + vtable_type_metadata } /// Creates debug information for the given vtable, which is for the @@ -2670,11 +2713,12 @@ pub fn create_vtable_metadata<'ll, 'tcx>( return; } - let vtable_name = compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref); + let vtable_name = + compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable); let vtable_type = vtable_type_metadata(cx, ty, poly_trait_ref); + let linkage_name = ""; unsafe { - let linkage_name = ""; llvm::LLVMRustDIBuilderCreateStaticVariable( DIB(cx), NO_SCOPE_METADATA, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 01f7868df3..247cb9ee6e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -25,7 +25,7 @@ use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_index::vec::IndexVec; use rustc_middle::mir; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeFoldable}; use rustc_session::config::{self, DebugInfo}; @@ -108,18 +108,29 @@ impl<'a, 'tcx> CrateDebugContext<'a, 'tcx> { // This can be overridden using --llvm-opts -dwarf-version,N. // Android has the same issue (#22398) if let Some(version) = sess.target.dwarf_version { - llvm::LLVMRustAddModuleFlag(self.llmod, "Dwarf Version\0".as_ptr().cast(), version) + llvm::LLVMRustAddModuleFlag( + self.llmod, + llvm::LLVMModFlagBehavior::Warning, + "Dwarf Version\0".as_ptr().cast(), + version, + ) } // Indicate that we want CodeView debug information on MSVC if sess.target.is_like_msvc { - llvm::LLVMRustAddModuleFlag(self.llmod, "CodeView\0".as_ptr().cast(), 1) + llvm::LLVMRustAddModuleFlag( + self.llmod, + llvm::LLVMModFlagBehavior::Warning, + "CodeView\0".as_ptr().cast(), + 1, + ) } // Prevent bitcode readers from deleting the debug info. let ptr = "Debug Info Version\0".as_ptr(); llvm::LLVMRustAddModuleFlag( self.llmod, + llvm::LLVMModFlagBehavior::Warning, ptr.cast(), llvm::LLVMRustDebugMetadataVersion(), ); @@ -307,9 +318,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn_abi: &FnAbi<'tcx, Ty<'tcx>>, maybe_definition_llfn: Option<&'ll Value>, ) -> &'ll DIScope { + let tcx = self.tcx; + let def_id = instance.def_id(); let containing_scope = get_containing_scope(self, instance); - let span = self.tcx.def_span(def_id); + let span = tcx.def_span(def_id); let loc = self.lookup_debug_loc(span.lo()); let file_metadata = file_metadata(self, &loc.file); @@ -319,16 +332,24 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { }; let mut name = String::new(); - type_names::push_item_name(self.tcx(), def_id, false, &mut name); + type_names::push_item_name(tcx, def_id, false, &mut name); // Find the enclosing function, in case this is a closure. - let enclosing_fn_def_id = self.tcx().typeck_root_def_id(def_id); + let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); + + // We look up the generics of the enclosing function and truncate the substs + // to their length in order to cut off extra stuff that might be in there for + // closures or generators. + let generics = tcx.generics_of(enclosing_fn_def_id); + let substs = instance.substs.truncate_to(tcx, generics); + + type_names::push_generic_params( + tcx, + tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs), + &mut name, + ); - // Get_template_parameters() will append a `<...>` clause to the function - // name if necessary. - let generics = self.tcx().generics_of(enclosing_fn_def_id); - let substs = instance.substs.truncate_to(self.tcx(), generics); - let template_parameters = get_template_parameters(self, generics, substs, &mut name); + let template_parameters = get_template_parameters(self, generics, substs); let linkage_name = &mangled_name_of_instance(self, instance).name; // Omit the linkage_name if it is the same as subprogram name. @@ -350,7 +371,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { if self.sess().opts.optimize != config::OptLevel::No { spflags |= DISPFlags::SPFlagOptimized; } - if let Some((id, _)) = self.tcx.entry_fn(()) { + if let Some((id, _)) = tcx.entry_fn(()) { if id == def_id { spflags |= DISPFlags::SPFlagMainSubprogram; } @@ -390,7 +411,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { signature.push(if fn_abi.ret.is_ignore() { None } else { - Some(type_metadata(cx, fn_abi.ret.layout.ty, rustc_span::DUMMY_SP)) + Some(type_metadata(cx, fn_abi.ret.layout.ty)) }); // Arguments types @@ -409,21 +430,17 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { let t = arg.layout.ty; let t = match t.kind() { ty::Array(ct, _) - if (*ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => + if (*ct == cx.tcx.types.u8) || cx.layout_of(*ct).is_zst() => { - cx.tcx.mk_imm_ptr(ct) + cx.tcx.mk_imm_ptr(*ct) } _ => t, }; - Some(type_metadata(cx, t, rustc_span::DUMMY_SP)) + Some(type_metadata(cx, t)) })); } else { - signature.extend( - fn_abi - .args - .iter() - .map(|arg| Some(type_metadata(cx, arg.layout.ty, rustc_span::DUMMY_SP))), - ); + signature + .extend(fn_abi.args.iter().map(|arg| Some(type_metadata(cx, arg.layout.ty)))); } create_DIArray(DIB(cx), &signature[..]) @@ -433,14 +450,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { cx: &CodegenCx<'ll, 'tcx>, generics: &ty::Generics, substs: SubstsRef<'tcx>, - name_to_append_suffix_to: &mut String, ) -> &'ll DIArray { - type_names::push_generic_params( - cx.tcx, - cx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs), - name_to_append_suffix_to, - ); - if substs.types().next().is_none() { return create_DIArray(DIB(cx), &[]); } @@ -453,8 +463,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - let actual_type_metadata = - type_metadata(cx, actual_type, rustc_span::DUMMY_SP); + let actual_type_metadata = type_metadata(cx, actual_type); let name = name.as_str(); Some(unsafe { Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( @@ -507,9 +516,9 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { ty::Adt(def, ..) if !def.is_box() => { // Again, only create type information if full debuginfo is enabled if cx.sess().opts.debuginfo == DebugInfo::Full - && !impl_self_ty.definitely_needs_subst(cx.tcx) + && !impl_self_ty.needs_subst() { - Some(type_metadata(cx, impl_self_ty, rustc_span::DUMMY_SP)) + Some(type_metadata(cx, impl_self_ty)) } else { Some(namespace::item_namespace(cx, def.did)) } @@ -584,7 +593,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { let loc = self.lookup_debug_loc(span.lo()); let file_metadata = file_metadata(self, &loc.file); - let type_metadata = type_metadata(self, variable_type, span); + let type_metadata = type_metadata(self, variable_type); let (argument_index, dwarf_tag) = match variable_kind { ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 953b6765a4..acd032a7dc 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -4,7 +4,9 @@ use super::namespace::item_namespace; use super::CrateDebugContext; use rustc_hir::def_id::DefId; -use rustc_middle::ty::DefIdTree; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, DefIdTree, Ty}; +use rustc_target::abi::Variants; use crate::common::CodegenCx; use crate::llvm; @@ -46,3 +48,53 @@ pub fn DIB<'a, 'll>(cx: &'a CodegenCx<'ll, '_>) -> &'a DIBuilder<'ll> { pub fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { item_namespace(cx, cx.tcx.parent(def_id).expect("get_namespace_for_item: missing parent?")) } + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum FatPtrKind { + Slice, + Dyn, +} + +/// Determines if `pointee_ty` is slice-like or trait-object-like, i.e. +/// if the second field of the fat pointer is a length or a vtable-pointer. +/// If `pointee_ty` does not require a fat pointer (because it is Sized) then +/// the function returns `None`. +pub(crate) fn fat_pointer_kind<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + pointee_ty: Ty<'tcx>, +) -> Option { + let layout = cx.layout_of(pointee_ty); + + if !layout.is_unsized() { + return None; + } + + match *pointee_ty.kind() { + ty::Str | ty::Slice(_) => Some(FatPtrKind::Slice), + ty::Dynamic(..) => Some(FatPtrKind::Dyn), + ty::Adt(..) | ty::Tuple(..) if matches!(layout.variants, Variants::Single { .. }) => { + let last_field_index = layout.fields.count() - 1; + debug_assert!( + (0..last_field_index) + .all(|field_index| { !layout.field(cx, field_index).is_unsized() }) + ); + + let unsized_field = layout.field(cx, last_field_index); + debug_assert!(unsized_field.is_unsized()); + fat_pointer_kind(cx, unsized_field.ty) + } + ty::Foreign(_) => { + // Assert that pointers to foreign types really are thin: + debug_assert_eq!( + cx.size_of(cx.tcx.mk_imm_ptr(pointee_ty)), + cx.size_of(cx.tcx.mk_imm_ptr(cx.tcx.types.u8)) + ); + None + } + _ => { + // For all other pointee types we should already have returned None + // at the beginning of the function. + panic!("fat_pointer_kind() - Encountered unexpected `pointee_ty`: {:?}", pointee_ty) + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 90d0d5caba..a6e06ffa81 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -47,6 +47,7 @@ fn declare_raw_fn<'ll>( attributes::default_optimisation_attrs(cx.tcx.sess, llfn); attributes::non_lazy_bind(cx.sess(), llfn); + llfn } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 07d49b6e72..cfd23f5c24 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -7,7 +7,6 @@ use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; use crate::value::Value; -use rustc_ast as ast; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh}; use rustc_codegen_ssa::common::span_invalid_monomorphization_error; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; @@ -351,7 +350,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.type_void(), true, false, - ast::LlvmAsmDialect::Att, + llvm::AsmDialect::Att, &[span], false, None, @@ -526,9 +525,8 @@ fn codegen_msvc_try<'ll>( normal.ret(bx.const_i32(0)); - let cs = catchswitch.catch_switch(None, None, 2); - catchswitch.add_handler(cs, catchpad_rust.llbb()); - catchswitch.add_handler(cs, catchpad_foreign.llbb()); + let cs = + catchswitch.catch_switch(None, None, &[catchpad_rust.llbb(), catchpad_foreign.llbb()]); // We can't use the TypeDescriptor defined in libpanic_unwind because it // might be in another DLL and the SEH encoding only supports specifying @@ -791,7 +789,7 @@ fn get_rust_try_fn<'ll, 'tcx>( ))); // `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32` let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig( - vec![try_fn_ty, i8p, catch_fn_ty].into_iter(), + [try_fn_ty, i8p, catch_fn_ty].into_iter(), tcx.types.i32, false, hir::Unsafety::Unsafe, @@ -1134,8 +1132,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( fn simd_simple_float_intrinsic<'ll, 'tcx>( name: Symbol, - in_elem: &::rustc_middle::ty::TyS<'_>, - in_ty: &::rustc_middle::ty::TyS<'_>, + in_elem: Ty<'_>, + in_ty: Ty<'_>, in_len: u64, bx: &mut Builder<'_, 'll, 'tcx>, span: Span, @@ -1689,7 +1687,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, bitwise_red!(simd_reduce_all: vector_reduce_and, true); bitwise_red!(simd_reduce_any: vector_reduce_or, true); - if name == sym::simd_cast { + if name == sym::simd_cast || name == sym::simd_as { require_simd!(ret_ty, "return"); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); require!( @@ -1715,14 +1713,26 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, let (in_style, in_width) = match in_elem.kind() { // vectors of pointer-sized integers should've been // disallowed before here, so this unwrap is safe. - ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::Int(i) => ( + Style::Int(true), + i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Uint(u) => ( + Style::Int(false), + u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), ty::Float(f) => (Style::Float, f.bit_width()), _ => (Style::Unsupported, 0), }; let (out_style, out_width) = match out_elem.kind() { - ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::Int(i) => ( + Style::Int(true), + i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Uint(u) => ( + Style::Int(false), + u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), ty::Float(f) => (Style::Float, f.bit_width()), _ => (Style::Unsupported, 0), }; @@ -1749,10 +1759,10 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, }); } (Style::Float, Style::Int(out_is_signed)) => { - return Ok(if out_is_signed { - bx.fptosi(args[0].immediate(), llret_ty) - } else { - bx.fptoui(args[0].immediate(), llret_ty) + return Ok(match (out_is_signed, name == sym::simd_as) { + (false, false) => bx.fptoui(args[0].immediate(), llret_ty), + (true, false) => bx.fptosi(args[0].immediate(), llret_ty), + (_, true) => bx.cast_float_to_int(out_is_signed, args[0].immediate(), llret_ty), }); } (Style::Float, Style::Float) => { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index cea4595fbb..75836e1438 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -7,9 +7,11 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(let_else)] #![feature(extern_types)] #![feature(nll)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] use back::write::{create_informational_target_machine, create_target_machine}; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index f2782f84f5..657f1fcf31 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -61,6 +61,26 @@ pub enum LLVMMachineType { ARM = 0x01c0, } +/// LLVM's Module::ModFlagBehavior, defined in llvm/include/llvm/IR/Module.h. +/// +/// When merging modules (e.g. during LTO), their metadata flags are combined. Conflicts are +/// resolved according to the merge behaviors specified here. Flags differing only in merge +/// behavior are still considered to be in conflict. +/// +/// In order for Rust-C LTO to work, we must specify behaviors compatible with Clang. Notably, +/// 'Error' and 'Warning' cannot be mixed for a given flag. +#[derive(Copy, Clone, PartialEq)] +#[repr(C)] +pub enum LLVMModFlagBehavior { + Error = 1, + Warning = 2, + Require = 3, + Override = 4, + Append = 5, + AppendUnique = 6, + Max = 7, +} + // Consts for the LLVM CallConv type, pre-cast to usize. /// LLVM CallingConv::ID. Should we wrap this? @@ -169,6 +189,8 @@ pub enum Attribute { StackProtectReq = 30, StackProtectStrong = 31, StackProtect = 32, + NoUndef = 33, + SanitizeMemTag = 34, } /// LLVMIntPredicate @@ -423,22 +445,13 @@ pub enum MetadataType { } /// LLVMRustAsmDialect -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq)] #[repr(C)] pub enum AsmDialect { Att, Intel, } -impl AsmDialect { - pub fn from_generic(asm: rustc_ast::LlvmAsmDialect) -> Self { - match asm { - rustc_ast::LlvmAsmDialect::Att => AsmDialect::Att, - rustc_ast::LlvmAsmDialect::Intel => AsmDialect::Intel, - } - } -} - /// LLVMRustCodeGenOptLevel #[derive(Copy, Clone, PartialEq)] #[repr(C)] @@ -976,6 +989,7 @@ pub type SelfProfileAfterPassCallback = unsafe extern "C" fn(*mut c_void); extern "C" { pub fn LLVMRustInstallFatalErrorHandler(); + pub fn LLVMRustDisableSystemDialogsOnCrash(); // Create and destroy contexts. pub fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; @@ -1169,6 +1183,7 @@ extern "C" { pub fn LLVMRustAddByValAttr(Fn: &Value, index: c_uint, ty: &Type); pub fn LLVMRustAddStructRetAttr(Fn: &Value, index: c_uint, ty: &Type); pub fn LLVMRustAddFunctionAttribute(Fn: &Value, index: c_uint, attr: Attribute); + pub fn LLVMRustEmitUWTableAttr(Fn: &Value, async_: bool); pub fn LLVMRustAddFunctionAttrStringValue( Fn: &Value, index: c_uint, @@ -1904,7 +1919,16 @@ extern "C" { pub fn LLVMRustIsRustLLVM() -> bool; - pub fn LLVMRustAddModuleFlag(M: &Module, name: *const c_char, value: u32); + /// Add LLVM module flags. + /// + /// In order for Rust-C LTO to work, module flags must be compatible with Clang. What + /// "compatible" means depends on the merge behaviors involved. + pub fn LLVMRustAddModuleFlag( + M: &Module, + merge_behavior: LLVMModFlagBehavior, + name: *const c_char, + value: u32, + ); pub fn LLVMRustMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; @@ -2329,7 +2353,6 @@ extern "C" { pub fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); pub fn LLVMRustAddAlwaysInlinePass(P: &PassManagerBuilder, AddLifetimes: bool); pub fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); - pub fn LLVMRustMarkAllFunctionsNounwind(M: &Module); pub fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>; pub fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index a1117a11fc..8586b0466c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -31,6 +31,10 @@ impl LLVMRustResult { } } +pub fn EmitUWTableAttr(llfn: &Value, async_: bool) { + unsafe { LLVMRustEmitUWTableAttr(llfn, async_) } +} + pub fn AddFunctionAttrStringValue(llfn: &Value, idx: AttributePlace, attr: &CStr, value: &CStr) { unsafe { LLVMRustAddFunctionAttrStringValue(llfn, idx.as_uint(), attr.as_ptr(), value.as_ptr()) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index d49df29f45..5f39d58745 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -2,8 +2,8 @@ use crate::back::write::create_informational_target_machine; use crate::{llvm, llvm_util}; use libc::c_int; use libloading::Library; -use rustc_codegen_ssa::target_features::supported_target_features; -use rustc_data_structures::fx::FxHashSet; +use rustc_codegen_ssa::target_features::{supported_target_features, tied_target_features}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::config::PrintRequest; @@ -46,6 +46,12 @@ unsafe fn configure_llvm(sess: &Session) { let mut llvm_args = Vec::with_capacity(n_args + 1); llvm::LLVMRustInstallFatalErrorHandler(); + // On Windows, an LLVM assertion will open an Abort/Retry/Ignore dialog + // box for the purpose of launching a debugger. However, on CI this will + // cause it to hang until it times out, which can take several hours. + if std::env::var_os("CI").is_some() { + llvm::LLVMRustDisableSystemDialogsOnCrash(); + } fn llvm_arg_to_arg_name(full_arg: &str) -> &str { full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("") @@ -185,30 +191,58 @@ pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> Vec<&'a str> { ("aarch64", "frintts") => vec!["fptoint"], ("aarch64", "fcma") => vec!["complxnum"], ("aarch64", "pmuv3") => vec!["perfmon"], + ("aarch64", "paca") => vec!["pauth"], + ("aarch64", "pacg") => vec!["pauth"], (_, s) => vec![s], } } +// Given a map from target_features to whether they are enabled or disabled, +// ensure only valid combinations are allowed. +pub fn check_tied_features( + sess: &Session, + features: &FxHashMap<&str, bool>, +) -> Option<&'static [&'static str]> { + for tied in tied_target_features(sess) { + // Tied features must be set to the same value, or not set at all + let mut tied_iter = tied.iter(); + let enabled = features.get(tied_iter.next().unwrap()); + + if tied_iter.any(|f| enabled != features.get(f)) { + return Some(tied); + } + } + None +} + pub fn target_features(sess: &Session) -> Vec { let target_machine = create_informational_target_machine(sess); - supported_target_features(sess) - .iter() - .filter_map( - |&(feature, gate)| { + let mut features: Vec = + supported_target_features(sess) + .iter() + .filter_map(|&(feature, gate)| { if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None } - }, - ) - .filter(|feature| { - for llvm_feature in to_llvm_feature(sess, feature) { - let cstr = CString::new(llvm_feature).unwrap(); - if unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } { - return true; + }) + .filter(|feature| { + for llvm_feature in to_llvm_feature(sess, feature) { + let cstr = CString::new(llvm_feature).unwrap(); + if unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } { + return true; + } } - } - false - }) - .map(|feature| Symbol::intern(feature)) - .collect() + false + }) + .map(|feature| Symbol::intern(feature)) + .collect(); + + // LLVM 14 changed the ABI for i128 arguments to __float/__fix builtins on Win64 + // (see https://reviews.llvm.org/D110413). This unstable target feature is intended for use + // by compiler-builtins, to export the builtins with the expected, LLVM-version-dependent ABI. + // The target feature can be dropped once we no longer support older LLVM versions. + if sess.is_nightly_build() && get_version() >= (14, 0, 0) { + features.push(Symbol::intern("llvm14-builtins-abi")); + } + features } pub fn print_version() { @@ -389,15 +423,19 @@ pub fn llvm_global_features(sess: &Session) -> Vec { Some(_) | None => {} }; + fn strip(s: &str) -> &str { + s.strip_prefix(&['+', '-']).unwrap_or(s) + } + let filter = |s: &str| { if s.is_empty() { return vec![]; } - let feature = if s.starts_with('+') || s.starts_with('-') { - &s[1..] - } else { + let feature = strip(s); + if feature == s { return vec![s.to_string()]; - }; + } + // Rustc-specific feature requests like `+crt-static` or `-crt-static` // are not passed down to LLVM. if RUSTC_SPECIFIC_FEATURES.contains(&feature) { @@ -414,8 +452,17 @@ pub fn llvm_global_features(sess: &Session) -> Vec { features.extend(sess.target.features.split(',').flat_map(&filter)); // -Ctarget-features - features.extend(sess.opts.cg.target_feature.split(',').flat_map(&filter)); - + let feats: Vec<&str> = sess.opts.cg.target_feature.split(',').collect(); + // LLVM enables based on the last occurence of a feature + if let Some(f) = + check_tied_features(sess, &feats.iter().map(|f| (strip(f), !f.starts_with("-"))).collect()) + { + sess.err(&format!( + "Target features {} must all be enabled or disabled together", + f.join(", ") + )); + } + features.extend(feats.iter().flat_map(|&f| filter(f))); features } @@ -428,8 +475,9 @@ pub(crate) fn should_use_new_llvm_pass_manager(user_opt: &Option, target_a // The new pass manager is enabled by default for LLVM >= 13. // This matches Clang, which also enables it since Clang 13. - // FIXME: There are some perf issues with the new pass manager - // when targeting s390x, so it is temporarily disabled for that - // arch, see https://github.com/rust-lang/rust/issues/89609 - user_opt.unwrap_or_else(|| target_arch != "s390x" && llvm_util::get_version() >= (13, 0, 0)) + // There are some perf issues with the new pass manager when targeting + // s390x with LLVM 13, so enable the new pass manager only with LLVM 14. + // See https://github.com/rust-lang/rust/issues/89609. + let min_version = if target_arch == "s390x" { 14 } else { 13 }; + user_opt.unwrap_or_else(|| llvm_util::get_version() >= (min_version, 0, 0)) } diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index f8c919ec2a..fafb9a6dbd 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -49,7 +49,7 @@ fn uncached_llvm_type<'a, 'tcx>( (layout.ty.kind(), &layout.variants) { if def.is_enum() && !def.variants.is_empty() { - write!(&mut name, "::{}", def.variants[index].ident).unwrap(); + write!(&mut name, "::{}", def.variants[index].name).unwrap(); } } if let (&ty::Generator(_, _, _), &Variants::Single { index }) = @@ -330,7 +330,9 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { ty::Ref(..) | ty::RawPtr(_) => { return self.field(cx, index).llvm_type(cx); } - ty::Adt(def, _) if def.is_box() => { + // only wide pointer boxes are handled as pointers + // thin pointer boxes with scalar allocators are handled by the general logic below + ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => { let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty()); return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate); } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 5c13dfdc1b..8bbf25ce03 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -9,12 +9,12 @@ test = false [dependencies] bitflags = "1.2.1" cc = "1.0.69" -itertools = "0.9" +itertools = "0.10" tracing = "0.1" libc = "0.2.50" jobserver = "0.1.22" tempfile = "3.2" -thorin-dwp = "0.1.1" +thorin-dwp = "0.2" pathdiff = "0.2.0" snap = "1" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } @@ -41,6 +41,6 @@ rustc_target = { path = "../rustc_target" } rustc_session = { path = "../rustc_session" } [dependencies.object] -version = "0.26.2" +version = "0.28.0" default-features = false features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 3db948a16f..a2f74b9421 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -51,7 +51,6 @@ pub trait ArchiveBuilder<'a> { fn add_archive(&mut self, archive: &Path, skip: F) -> io::Result<()> where F: FnMut(&str) -> bool + 'static; - fn update_symbols(&mut self); fn build(self); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f7fe194d20..58e0667d67 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -216,17 +216,18 @@ pub fn each_linked_rlib( } let name = &info.crate_name[&cnum]; let used_crate_source = &info.used_crate_source[&cnum]; - let path = if let Some((path, _)) = &used_crate_source.rlib { - path - } else if used_crate_source.rmeta.is_some() { - return Err(format!( - "could not find rlib for: `{}`, found rmeta (metadata) file", - name - )); + if let Some((path, _)) = &used_crate_source.rlib { + f(cnum, &path); } else { - return Err(format!("could not find rlib for: `{}`", name)); - }; - f(cnum, &path); + if used_crate_source.rmeta.is_some() { + return Err(format!( + "could not find rlib for: `{}`, found rmeta (metadata) file", + name + )); + } else { + return Err(format!("could not find rlib for: `{}`", name)); + } + } } Ok(()) } @@ -333,10 +334,6 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir); } - // After adding all files to the archive, we need to update the - // symbol table of the archive. - ab.update_symbols(); - // Note that it is important that we add all of our non-object "magical // files" *after* all of the object files in the archive. The reason for // this is as follows: @@ -365,13 +362,6 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( // normal linkers for the platform. let metadata = create_rmeta_file(sess, codegen_results.metadata.raw_data()); ab.add_file(&emit_metadata(sess, &metadata, tmpdir)); - - // After adding all files to the archive, we need to update the - // symbol table of the archive. This currently dies on macOS (see - // #11162), and isn't necessary there anyway - if !sess.target.is_like_osx { - ab.update_symbols(); - } } RlibFlavor::StaticlibBase => { @@ -381,6 +371,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( } } } + return Ok(ab); } @@ -509,7 +500,6 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>( sess.fatal(&e); } - ab.update_symbols(); ab.build(); if !all_native_libs.is_empty() { @@ -667,7 +657,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( cmd.env_remove(k); } - if sess.opts.debugging_opts.print_link_args { + if sess.opts.prints.contains(&PrintRequest::LinkArgs) { println!("{:?}", &cmd); } @@ -932,7 +922,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( but `link.exe` was not found", ); sess.note_without_error( - "please ensure that VS 2013, VS 2015, VS 2017 or VS 2019 \ + "please ensure that VS 2013, VS 2015, VS 2017, VS 2019 or VS 2022 \ was installed with the Visual C++ option", ); } @@ -1159,6 +1149,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { LinkerFlavor::Lld(_) => "lld", LinkerFlavor::PtxLinker => "rust-ptx-linker", LinkerFlavor::BpfLinker => "bpf-linker", + LinkerFlavor::L4Bender => "l4-bender", }), flavor, )), @@ -2309,7 +2300,6 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| { let mut archive = ::new(sess, &dst, Some(cratepath)); - archive.update_symbols(); let mut any_objects = false; for f in archive.src_files() { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 15d16e7d3d..3fb56f42b8 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -126,7 +126,6 @@ pub fn get_linker<'a>( // FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction // to the linker args construction. assert!(cmd.get_args().is_empty() || sess.target.vendor == "uwp"); - match flavor { LinkerFlavor::Lld(LldFlavor::Link) | LinkerFlavor::Msvc => { Box::new(MsvcLinker { cmd, sess }) as Box @@ -149,6 +148,8 @@ pub fn get_linker<'a>( LinkerFlavor::PtxLinker => Box::new(PtxLinker { cmd, sess }) as Box, LinkerFlavor::BpfLinker => Box::new(BpfLinker { cmd, sess }) as Box, + + LinkerFlavor::L4Bender => Box::new(L4Bender::new(cmd, sess)) as Box, } } @@ -1355,6 +1356,157 @@ impl<'a> Linker for WasmLd<'a> { } } +/// Linker shepherd script for L4Re (Fiasco) +pub struct L4Bender<'a> { + cmd: Command, + sess: &'a Session, + hinted_static: bool, +} + +impl<'a> Linker for L4Bender<'a> { + fn link_dylib(&mut self, _lib: Symbol, _verbatim: bool, _as_needed: bool) { + bug!("dylibs are not supported on L4Re"); + } + fn link_staticlib(&mut self, lib: Symbol, _verbatim: bool) { + self.hint_static(); + self.cmd.arg(format!("-PC{}", lib)); + } + fn link_rlib(&mut self, lib: &Path) { + self.hint_static(); + self.cmd.arg(lib); + } + fn include_path(&mut self, path: &Path) { + self.cmd.arg("-L").arg(path); + } + fn framework_path(&mut self, _: &Path) { + bug!("frameworks are not supported on L4Re"); + } + fn output_filename(&mut self, path: &Path) { + self.cmd.arg("-o").arg(path); + } + + fn add_object(&mut self, path: &Path) { + self.cmd.arg(path); + } + + fn full_relro(&mut self) { + self.cmd.arg("-zrelro"); + self.cmd.arg("-znow"); + } + + fn partial_relro(&mut self) { + self.cmd.arg("-zrelro"); + } + + fn no_relro(&mut self) { + self.cmd.arg("-znorelro"); + } + + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + + fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} + + fn link_rust_dylib(&mut self, _: Symbol, _: &Path) { + panic!("Rust dylibs not supported"); + } + + fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) { + bug!("frameworks not supported on L4Re"); + } + + fn link_whole_staticlib(&mut self, lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) { + self.hint_static(); + self.cmd.arg("--whole-archive").arg(format!("-l{}", lib)); + self.cmd.arg("--no-whole-archive"); + } + + fn link_whole_rlib(&mut self, lib: &Path) { + self.hint_static(); + self.cmd.arg("--whole-archive").arg(lib).arg("--no-whole-archive"); + } + + fn gc_sections(&mut self, keep_metadata: bool) { + if !keep_metadata { + self.cmd.arg("--gc-sections"); + } + } + + fn no_gc_sections(&mut self) { + self.cmd.arg("--no-gc-sections"); + } + + fn optimize(&mut self) { + // GNU-style linkers support optimization with -O. GNU ld doesn't + // need a numeric argument, but other linkers do. + if self.sess.opts.optimize == config::OptLevel::Default + || self.sess.opts.optimize == config::OptLevel::Aggressive + { + self.cmd.arg("-O1"); + } + } + + fn pgo_gen(&mut self) {} + + fn debuginfo(&mut self, strip: Strip) { + match strip { + Strip::None => {} + Strip::Debuginfo => { + self.cmd().arg("--strip-debug"); + } + Strip::Symbols => { + self.cmd().arg("--strip-all"); + } + } + } + + fn no_default_libraries(&mut self) { + self.cmd.arg("-nostdlib"); + } + + fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) { + // ToDo, not implemented, copy from GCC + self.sess.warn("exporting symbols not implemented yet for L4Bender"); + return; + } + + fn subsystem(&mut self, subsystem: &str) { + self.cmd.arg(&format!("--subsystem {}", subsystem)); + } + + fn reset_per_library_state(&mut self) { + self.hint_static(); // Reset to default before returning the composed command line. + } + + fn group_start(&mut self) { + self.cmd.arg("--start-group"); + } + + fn group_end(&mut self) { + self.cmd.arg("--end-group"); + } + + fn linker_plugin_lto(&mut self) {} + + fn control_flow_guard(&mut self) {} + + fn no_crt_objects(&mut self) {} +} + +impl<'a> L4Bender<'a> { + pub fn new(cmd: Command, sess: &'a Session) -> L4Bender<'a> { + L4Bender { cmd: cmd, sess: sess, hinted_static: false } + } + + fn hint_static(&mut self) { + if !self.hinted_static { + self.cmd.arg("-static"); + self.hinted_static = true; + } + } +} + pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { if let Some(ref exports) = tcx.sess.target.override_export_symbols { return exports.clone(); diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 79c24f0f17..9ebbcac76a 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -95,7 +95,7 @@ fn search_for_metadata<'a>( .map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e)) } -fn create_object_file(sess: &Session) -> Option { +fn create_object_file(sess: &Session) -> Option> { let endianness = match sess.target.options.endian { Endian::Little => Endianness::Little, Endian::Big => Endianness::Big, @@ -135,12 +135,24 @@ fn create_object_file(sess: &Session) -> Option { Architecture::Mips => { // copied from `mipsel-linux-gnu-gcc foo.c -c` and // inspecting the resulting `e_flags` field. - let e_flags = elf::EF_MIPS_ARCH_32R2 | elf::EF_MIPS_CPIC | elf::EF_MIPS_PIC; + let e_flags = elf::EF_MIPS_CPIC + | elf::EF_MIPS_PIC + | if sess.target.options.cpu.contains("r6") { + elf::EF_MIPS_ARCH_32R6 | elf::EF_MIPS_NAN2008 + } else { + elf::EF_MIPS_ARCH_32R2 + }; file.flags = FileFlags::Elf { e_flags }; } Architecture::Mips64 => { // copied from `mips64el-linux-gnuabi64-gcc foo.c -c` - let e_flags = elf::EF_MIPS_ARCH_64R2 | elf::EF_MIPS_CPIC | elf::EF_MIPS_PIC; + let e_flags = elf::EF_MIPS_CPIC + | elf::EF_MIPS_PIC + | if sess.target.options.cpu.contains("r6") { + elf::EF_MIPS_ARCH_64R6 | elf::EF_MIPS_NAN2008 + } else { + elf::EF_MIPS_ARCH_64R2 + }; file.flags = FileFlags::Elf { e_flags }; } Architecture::Riscv64 if sess.target.options.features.contains("+d") => { @@ -188,9 +200,7 @@ fn create_object_file(sess: &Session) -> Option { // `SHF_EXCLUDE` flag we can set on sections in an object file to get // automatically removed from the final output. pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec { - let mut file = if let Some(file) = create_object_file(sess) { - file - } else { + let Some(mut file) = create_object_file(sess) else { // This is used to handle all "other" targets. This includes targets // in two categories: // @@ -250,9 +260,7 @@ pub fn create_compressed_metadata_file( ) -> Vec { let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap(); - let mut file = if let Some(file) = create_object_file(sess) { - file - } else { + let Some(mut file) = create_object_file(sess) else { return compressed.to_vec(); }; let section = file.add_section( diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index baafa74b13..aeddd92689 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -76,7 +76,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap< // // As a result, if this id is an FFI item (foreign item) then we only // let it through if it's included statically. - match tcx.hir().get(tcx.hir().local_def_id_to_hir_id(def_id)) { + match tcx.hir().get_by_def_id(def_id) { Node::ForeignItem(..) => { tcx.is_statically_included_foreign_item(def_id).then_some(def_id) } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index bea454458c..f6fddbb509 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -32,7 +32,7 @@ use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span}; -use rustc_target::spec::{MergeFunctions, PanicStrategy, SanitizerSet}; +use rustc_target::spec::{MergeFunctions, SanitizerSet}; use std::any::Any; use std::fs; @@ -313,7 +313,6 @@ pub struct CodegenContext { pub backend: B, pub prof: SelfProfilerRef, pub lto: Lto, - pub no_landing_pads: bool, pub save_temps: bool, pub fewer_names: bool, pub time_trace: bool, @@ -478,7 +477,7 @@ pub fn start_async_codegen( codegen_worker_receive, shared_emitter_main, future: coordinator_thread, - output_filenames: tcx.output_filenames(()), + output_filenames: tcx.output_filenames(()).clone(), } } @@ -1039,7 +1038,6 @@ fn start_executing_work( crate_types: sess.crate_types().to_vec(), each_linked_rlib_for_lto, lto: sess.lto(), - no_landing_pads: sess.panic_strategy() == PanicStrategy::Abort, fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps, time_trace: sess.opts.debugging_opts.llvm_time_trace, @@ -1052,7 +1050,7 @@ fn start_executing_work( cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(), coordinator_send, diag_emitter: shared_emitter.clone(), - output_filenames: tcx.output_filenames(()), + output_filenames: tcx.output_filenames(()).clone(), regular_module_config: regular_config, metadata_module_config: metadata_config, allocator_module_config: allocator_config, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 49b785afa6..4a5eabc875 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -843,7 +843,7 @@ impl CrateInfo { used_crate_source: Default::default(), lang_item_to_crate: Default::default(), missing_lang_items: Default::default(), - dependency_formats: tcx.dependency_formats(()), + dependency_formats: tcx.dependency_formats(()).clone(), windows_subsystem, }; let lang_items = tcx.lang_items(); @@ -860,7 +860,7 @@ impl CrateInfo { info.native_libraries .insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect()); info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); - info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum)); + info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum).clone()); if tcx.is_compiler_builtins(cnum) { info.compiler_builtins = Some(cnum); } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 93bb1aee25..b63851c195 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -13,9 +13,9 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData}; +use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt}; @@ -102,14 +102,14 @@ fn push_debuginfo_type_name<'tcx>( ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { if cpp_like_debuginfo { match mutbl { - hir::Mutability::Not => output.push_str("ptr_const$<"), - hir::Mutability::Mut => output.push_str("ptr_mut$<"), + Mutability::Not => output.push_str("ptr_const$<"), + Mutability::Mut => output.push_str("ptr_mut$<"), } } else { output.push('*'); match mutbl { - hir::Mutability::Not => output.push_str("const "), - hir::Mutability::Mut => output.push_str("mut "), + Mutability::Not => output.push_str("const "), + Mutability::Mut => output.push_str("mut "), } } @@ -131,8 +131,8 @@ fn push_debuginfo_type_name<'tcx>( output.push_str(mutbl.prefix_str()); } else if !is_slice_or_str { match mutbl { - hir::Mutability::Not => output.push_str("ref$<"), - hir::Mutability::Mut => output.push_str("ref_mut$<"), + Mutability::Not => output.push_str("ref$<"), + Mutability::Mut => output.push_str("ref_mut$<"), } } @@ -146,7 +146,7 @@ fn push_debuginfo_type_name<'tcx>( if cpp_like_debuginfo { output.push_str("array$<"); push_debuginfo_type_name(tcx, inner_type, true, output, visited); - match len.val { + match len.val() { ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(), _ => write!(output, ",{}>", len.eval_usize(tcx, ty::ParamEnv::reveal_all())) .unwrap(), @@ -154,7 +154,7 @@ fn push_debuginfo_type_name<'tcx>( } else { output.push('['); push_debuginfo_type_name(tcx, inner_type, true, output, visited); - match len.val { + match len.val() { ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(), _ => write!(output, "; {}]", len.eval_usize(tcx, ty::ParamEnv::reveal_all())) .unwrap(), @@ -203,8 +203,9 @@ fn push_debuginfo_type_name<'tcx>( let projection_bounds: SmallVec<[_; 4]> = trait_data .projection_bounds() .map(|bound| { - let ExistentialProjection { item_def_id, ty, .. } = bound.skip_binder(); - (item_def_id, ty) + let ExistentialProjection { item_def_id, term, .. } = bound.skip_binder(); + // FIXME(associated_const_equality): allow for consts here + (item_def_id, term.ty().unwrap()) }) .collect(); @@ -342,16 +343,41 @@ fn push_debuginfo_type_name<'tcx>( // We only care about avoiding recursing // directly back to the type we're currently // processing - visited.remove(t); + visited.remove(&t); } - ty::Closure(def_id, ..) | ty::Generator(def_id, ..) => { - let key = tcx.def_key(def_id); + ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { + // Name will be "{closure_env#0}", "{generator_env#0}", or + // "{async_fn_env#0}", etc. + let def_key = tcx.def_key(def_id); + if qualified { - let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; + let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id }; push_item_name(tcx, parent_def_id, true, output); output.push_str("::"); } - push_unqualified_item_name(tcx, def_id, key.disambiguated_data, output); + + let mut label = String::with_capacity(20); + write!(&mut label, "{}_env", generator_kind_label(tcx.generator_kind(def_id))).unwrap(); + + push_disambiguated_special_name( + &label, + def_key.disambiguated_data.disambiguator, + cpp_like_debuginfo, + output, + ); + + // We also need to add the generic arguments of the async fn/generator or + // the enclosing function (for closures or async blocks), so that we end + // up with a unique name for every instantiation. + + // Find the generics of the enclosing function, as defined in the source code. + let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); + let generics = tcx.generics_of(enclosing_fn_def_id); + + // Truncate the substs to the length of the above generics. This will cut off + // anything closure- or generator-specific. + let substs = substs.truncate_to(tcx, generics); + push_generic_params_internal(tcx, substs, output, visited); } // Type parameters from polymorphized functions. ty::Param(_) => { @@ -409,14 +435,14 @@ fn push_debuginfo_type_name<'tcx>( let max = dataful_discriminant_range.end; let max = tag.value.size(&tcx).truncate(max); - let dataful_variant_name = def.variants[*dataful_variant].ident.as_str(); + let dataful_variant_name = def.variants[*dataful_variant].name.as_str(); output.push_str(&format!(", {}, {}, {}", min, max, dataful_variant_name)); } else if let Variants::Single { index: variant_idx } = &layout.variants { // Uninhabited enums can't be constructed and should never need to be visualized so // skip this step for them. if def.variants.len() != 0 { - let variant = def.variants[*variant_idx].ident.as_str(); + let variant = def.variants[*variant_idx].name.as_str(); output.push_str(&format!(", {}", variant)); } @@ -443,7 +469,14 @@ fn push_debuginfo_type_name<'tcx>( } } -/// Computes a name for the global variable storing a vtable. +pub enum VTableNameKind { + // Is the name for the const/static holding the vtable? + GlobalVariable, + // Is the name for the type of the vtable? + Type, +} + +/// Computes a name for the global variable storing a vtable (or the type of that global variable). /// /// The name is of the form: /// @@ -452,10 +485,15 @@ fn push_debuginfo_type_name<'tcx>( /// or, when generating C++-like names: /// /// `impl$::vtable$` +/// +/// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just +/// `{vtable}`, so that the type and the corresponding global variable get assigned different +/// names. pub fn compute_debuginfo_vtable_name<'tcx>( tcx: TyCtxt<'tcx>, t: Ty<'tcx>, trait_ref: Option>, + kind: VTableNameKind, ) -> String { let cpp_like_debuginfo = cpp_like_debuginfo(tcx); @@ -488,7 +526,12 @@ pub fn compute_debuginfo_vtable_name<'tcx>( push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name); - let suffix = if cpp_like_debuginfo { "::vtable$" } else { "::{vtable}" }; + let suffix = match (cpp_like_debuginfo, kind) { + (true, VTableNameKind::GlobalVariable) => "::vtable$", + (false, VTableNameKind::GlobalVariable) => "::{vtable}", + (true, VTableNameKind::Type) => "::vtable_type$", + (false, VTableNameKind::Type) => "::{vtable_type}", + }; vtable_name.reserve_exact(suffix.len()); vtable_name.push_str(suffix); @@ -508,6 +551,29 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: & push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output); } +fn generator_kind_label(generator_kind: Option) -> &'static str { + match generator_kind { + Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) => "async_block", + Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) => "async_closure", + Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) => "async_fn", + Some(GeneratorKind::Gen) => "generator", + None => "closure", + } +} + +fn push_disambiguated_special_name( + label: &str, + disambiguator: u32, + cpp_like_debuginfo: bool, + output: &mut String, +) { + if cpp_like_debuginfo { + write!(output, "{}${}", label, disambiguator).unwrap(); + } else { + write!(output, "{{{}#{}}}", label, disambiguator).unwrap(); + } +} + fn push_unqualified_item_name( tcx: TyCtxt<'_>, def_id: DefId, @@ -518,36 +584,32 @@ fn push_unqualified_item_name( DefPathData::CrateRoot => { output.push_str(tcx.crate_name(def_id.krate).as_str()); } - DefPathData::ClosureExpr if tcx.generator_kind(def_id).is_some() => { - // Generators look like closures, but we want to treat them differently - // in the debug info. - if cpp_like_debuginfo(tcx) { - write!(output, "generator${}", disambiguated_data.disambiguator).unwrap(); - } else { - write!(output, "{{generator#{}}}", disambiguated_data.disambiguator).unwrap(); - } + DefPathData::ClosureExpr => { + let label = generator_kind_label(tcx.generator_kind(def_id)); + + push_disambiguated_special_name( + label, + disambiguated_data.disambiguator, + cpp_like_debuginfo(tcx), + output, + ); } _ => match disambiguated_data.data.name() { DefPathDataName::Named(name) => { output.push_str(name.as_str()); } DefPathDataName::Anon { namespace } => { - if cpp_like_debuginfo(tcx) { - write!(output, "{}${}", namespace, disambiguated_data.disambiguator).unwrap(); - } else { - write!(output, "{{{}#{}}}", namespace, disambiguated_data.disambiguator) - .unwrap(); - } + push_disambiguated_special_name( + namespace.as_str(), + disambiguated_data.disambiguator, + cpp_like_debuginfo(tcx), + output, + ); } }, }; } -// Pushes the generic parameters in the given `InternalSubsts` to the output string. -// This ignores region parameters, since they can't reliably be -// reconstructed for items from non-local crates. For local crates, this -// would be possible but with inlining and LTO we have to use the least -// common denominator - otherwise we would run into conflicts. fn push_generic_params_internal<'tcx>( tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, @@ -583,19 +645,19 @@ fn push_generic_params_internal<'tcx>( true } -fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: &mut String) { - match ct.val { +fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) { + match ct.val() { ty::ConstKind::Param(param) => { write!(output, "{}", param.name) } - _ => match ct.ty.kind() { + _ => match ct.ty().kind() { ty::Int(ity) => { - let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty); + let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty()); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; write!(output, "{}", val) } ty::Uint(_) => { - let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty); + let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty()); write!(output, "{}", val) } ty::Bool => { @@ -610,7 +672,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: let mut hasher = StableHasher::new(); hcx.while_hashing_spans(false, |hcx| { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - ct.val.hash_stable(hcx, &mut hasher); + ct.val().hash_stable(hcx, &mut hasher); }); }); // Let's only emit 64 bits of the hash value. That should be plenty for diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 350199f4e9..9bb8db076a 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -7,6 +7,7 @@ #![feature(nll)] #![feature(associated_type_bounds)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] //! This crate contains codegen code that is used by all codegen backends (LLVM and others). //! The backend-agnostic functions of this crate use functions defined in various traits that diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index b1b3f1d6d8..d768d4920c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -211,7 +211,6 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> PlaceContext::MutatingUse( MutatingUseContext::Store - | MutatingUseContext::LlvmAsmOutput | MutatingUseContext::AsmOutput | MutatingUseContext::Borrow | MutatingUseContext::AddressOf diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index dcfe5fcc2c..4c7a09ca1e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -477,6 +477,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup); } + fn codegen_abort_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + ) { + let span = terminator.source_info.span; + self.set_debug_loc(&mut bx, terminator.source_info); + + // Get the location information. + let location = self.get_caller_location(&mut bx, terminator.source_info).immediate(); + + // Obtain the panic entry point. + let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); + let llfn = bx.get_fn_addr(instance); + + // Codegen the actual panic invoke/call. + helper.do_call(self, &mut bx, fn_abi, llfn, &[location], None, None); + } + /// Returns `true` if this is indeed a panic intrinsic and codegen is done. fn codegen_panic_intrinsic( &mut self, @@ -1014,10 +1036,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::TerminatorKind::Resume => self.codegen_resume_terminator(helper, bx), mir::TerminatorKind::Abort => { - bx.abort(); - // `abort` does not terminate the block, so we still need to generate - // an `unreachable` terminator after it. - bx.unreachable(); + self.codegen_abort_terminator(helper, bx, terminator); } mir::TerminatorKind::Goto { target } => { @@ -1327,8 +1346,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut cp_bx = self.new_block(&format!("cp_funclet{:?}", bb)); ret_llbb = cs_bx.llbb(); - let cs = cs_bx.catch_switch(None, None, 1); - cs_bx.add_handler(cs, cp_bx.llbb()); + let cs = cs_bx.catch_switch(None, None, &[cp_bx.llbb()]); // The "null" here is actually a RTTI type descriptor for the // C++ personality function, but `catch (...)` has no type so @@ -1355,8 +1373,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let llpersonality = self.cx.eh_personality(); let llretty = self.landing_pad_type(); - let lp = bx.landing_pad(llretty, llpersonality, 1); - bx.set_cleanup(lp); + let lp = bx.cleanup_landing_pad(llretty, llpersonality); let slot = self.get_personality_slot(&mut bx); slot.storage_live(&mut bx); @@ -1477,7 +1494,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"), LocalRef::Operand(None) => { let dst_layout = bx.layout_of(self.monomorphized_place_ty(dst.as_ref())); - assert!(!dst_layout.ty.has_erasable_regions(self.cx.tcx())); + assert!(!dst_layout.ty.has_erasable_regions()); let place = PlaceRef::alloca(bx, dst_layout); place.storage_live(bx); self.codegen_transmute_into(bx, src, place); diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 93b39dc8e9..5cdf131b0b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -29,7 +29,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::ConstantKind::Ty(ct) => ct, mir::ConstantKind::Val(val, _) => return Ok(val), }; - match ct.val { + match ct.val() { ty::ConstKind::Unevaluated(ct) => self .cx .tcx() @@ -61,11 +61,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let c = ty::Const::from_value(bx.tcx(), val, ty); let values: Vec<_> = bx .tcx() - .destructure_const(ty::ParamEnv::reveal_all().and(&c)) + .destructure_const(ty::ParamEnv::reveal_all().and(c)) .fields .iter() .map(|field| { - if let Some(prim) = field.val.try_to_scalar() { + if let Some(prim) = field.val().try_to_scalar() { let layout = bx.layout_of(field_ty); let scalar = match layout.abi { Abi::Scalar(x) => x, @@ -78,7 +78,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) .collect(); let llval = bx.const_struct(&values, false); - (llval, c.ty) + (llval, c.ty()) }) .unwrap_or_else(|_| { bx.tcx().sess.span_err(span, "could not evaluate shuffle_indices at compile time"); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 3657f80c2d..c654232c10 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -369,6 +369,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + sym::const_allocate => { + // returns a null pointer at runtime. + bx.const_null(bx.type_i8p()) + } + + sym::const_deallocate => { + // nop at runtime. + return; + } + // This requires that atomic intrinsics follow a specific naming pattern: // "atomic_[_]", and no ordering means SeqCst name if name_str.starts_with("atomic_") => { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 1ef863e84a..814e4d626e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -209,7 +209,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let mut allocate_local = |local| { let decl = &mir.local_decls[local]; let layout = bx.layout_of(fx.monomorphize(decl.ty)); - assert!(!layout.ty.has_erasable_regions(cx.tcx())); + assert!(!layout.ty.has_erasable_regions()); if local == mir::RETURN_PLACE && fx.fn_abi.ret.is_indirect() { debug!("alloc: {:?} (return place) -> place", local); diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 4b07ed1a1e..6976999c0e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -429,87 +429,78 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let cx = self.cx; let tcx = self.cx.tcx(); - let result = match place_ref { - mir::PlaceRef { local, projection: [] } => match self.locals[local] { - LocalRef::Place(place) => { - return place; - } - LocalRef::UnsizedPlace(place) => { - return bx.load_operand(place).deref(cx); - } - LocalRef::Operand(..) => { + let mut base = 0; + let mut cg_base = match self.locals[place_ref.local] { + LocalRef::Place(place) => place, + LocalRef::UnsizedPlace(place) => bx.load_operand(place).deref(cx), + LocalRef::Operand(..) => { + if let Some(elem) = place_ref + .projection + .iter() + .enumerate() + .find(|elem| matches!(elem.1, mir::ProjectionElem::Deref)) + { + base = elem.0 + 1; + self.codegen_consume( + bx, + mir::PlaceRef { projection: &place_ref.projection[..elem.0], ..place_ref }, + ) + .deref(bx.cx()) + } else { bug!("using operand local {:?} as place", place_ref); } - }, - mir::PlaceRef { local, projection: [proj_base @ .., mir::ProjectionElem::Deref] } => { - // Load the pointer from its location. - self.codegen_consume(bx, mir::PlaceRef { local, projection: proj_base }) - .deref(bx.cx()) } - mir::PlaceRef { local, projection: &[ref proj_base @ .., elem] } => { - // FIXME turn this recursion into iteration - let cg_base = - self.codegen_place(bx, mir::PlaceRef { local, projection: proj_base }); - - match elem { - mir::ProjectionElem::Deref => bug!(), - mir::ProjectionElem::Field(ref field, _) => { - cg_base.project_field(bx, field.index()) - } - mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::from(index)); - let index = self.codegen_operand(bx, index); - let llindex = index.immediate(); - cg_base.project_index(bx, llindex) - } - mir::ProjectionElem::ConstantIndex { - offset, - from_end: false, - min_length: _, - } => { - let lloffset = bx.cx().const_usize(offset as u64); - cg_base.project_index(bx, lloffset) - } - mir::ProjectionElem::ConstantIndex { - offset, - from_end: true, - min_length: _, - } => { - let lloffset = bx.cx().const_usize(offset as u64); - let lllen = cg_base.len(bx.cx()); - let llindex = bx.sub(lllen, lloffset); - cg_base.project_index(bx, llindex) - } - mir::ProjectionElem::Subslice { from, to, from_end } => { - let mut subslice = - cg_base.project_index(bx, bx.cx().const_usize(from as u64)); - let projected_ty = - PlaceTy::from_ty(cg_base.layout.ty).projection_ty(tcx, elem).ty; - subslice.layout = bx.cx().layout_of(self.monomorphize(projected_ty)); - - if subslice.layout.is_unsized() { - assert!(from_end, "slice subslices should be `from_end`"); - subslice.llextra = Some(bx.sub( - cg_base.llextra.unwrap(), - bx.cx().const_usize((from as u64) + (to as u64)), - )); - } - - // Cast the place pointer type to the new - // array or slice type (`*[%_; new_len]`). - subslice.llval = bx.pointercast( - subslice.llval, - bx.cx().type_ptr_to(bx.cx().backend_type(subslice.layout)), - ); - - subslice + }; + for elem in place_ref.projection[base..].iter() { + cg_base = match elem.clone() { + mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()), + mir::ProjectionElem::Field(ref field, _) => { + cg_base.project_field(bx, field.index()) + } + mir::ProjectionElem::Index(index) => { + let index = &mir::Operand::Copy(mir::Place::from(index)); + let index = self.codegen_operand(bx, index); + let llindex = index.immediate(); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::ConstantIndex { offset, from_end: false, min_length: _ } => { + let lloffset = bx.cx().const_usize(offset as u64); + cg_base.project_index(bx, lloffset) + } + mir::ProjectionElem::ConstantIndex { offset, from_end: true, min_length: _ } => { + let lloffset = bx.cx().const_usize(offset as u64); + let lllen = cg_base.len(bx.cx()); + let llindex = bx.sub(lllen, lloffset); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::Subslice { from, to, from_end } => { + let mut subslice = cg_base.project_index(bx, bx.cx().const_usize(from as u64)); + let projected_ty = + PlaceTy::from_ty(cg_base.layout.ty).projection_ty(tcx, *elem).ty; + subslice.layout = bx.cx().layout_of(self.monomorphize(projected_ty)); + + if subslice.layout.is_unsized() { + assert!(from_end, "slice subslices should be `from_end`"); + subslice.llextra = Some(bx.sub( + cg_base.llextra.unwrap(), + bx.cx().const_usize((from as u64) + (to as u64)), + )); } - mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, v), + + // Cast the place pointer type to the new + // array or slice type (`*[%_; new_len]`). + subslice.llval = bx.pointercast( + subslice.llval, + bx.cx().type_ptr_to(bx.cx().backend_type(subslice.layout)), + ); + + subslice } - } - }; - debug!("codegen_place(place={:?}) => {:?}", place_ref, result); - result + mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, v), + }; + } + debug!("codegen_place(place={:?}) => {:?}", place_ref, cg_base); + cg_base } pub fn monomorphized_place_ty(&self, place_ref: mir::PlaceRef<'tcx>) -> Ty<'tcx> { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 679c457670..68decce82a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -3,11 +3,10 @@ use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; use crate::base; -use crate::common::{self, IntPredicate, RealPredicate}; +use crate::common::{self, IntPredicate}; use crate::traits::*; use crate::MemFlags; -use rustc_apfloat::{ieee, Float, Round, Status}; use rustc_middle::mir; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; @@ -368,10 +367,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inttoptr(usize_llval, ll_t_out) } (CastTy::Float, CastTy::Int(IntTy::I)) => { - cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out) + bx.cast_float_to_int(true, llval, ll_t_out) } (CastTy::Float, CastTy::Int(_)) => { - cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out) + bx.cast_float_to_int(false, llval, ll_t_out) } _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty), }; @@ -768,146 +767,3 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // (*) this is only true if the type is suitable } } - -fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - signed: bool, - x: Bx::Value, - float_ty: Bx::Type, - int_ty: Bx::Type, -) -> Bx::Value { - if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { - return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - } - - let try_sat_result = if signed { bx.fptosi_sat(x, int_ty) } else { bx.fptoui_sat(x, int_ty) }; - if let Some(try_sat_result) = try_sat_result { - return try_sat_result; - } - - let int_width = bx.cx().int_width(int_ty); - let float_width = bx.cx().float_width(float_ty); - // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the - // destination integer type after rounding towards zero. This `undef` value can cause UB in - // safe code (see issue #10184), so we implement a saturating conversion on top of it: - // Semantically, the mathematical value of the input is rounded towards zero to the next - // mathematical integer, and then the result is clamped into the range of the destination - // integer type. Positive and negative infinity are mapped to the maximum and minimum value of - // the destination integer type. NaN is mapped to 0. - // - // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to - // a value representable in int_ty. - // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. - // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. - // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly - // representable. Note that this only works if float_ty's exponent range is sufficiently large. - // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 - // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. - // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because - // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). - // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. - let int_max = |signed: bool, int_width: u64| -> u128 { - let shift_amount = 128 - int_width; - if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount } - }; - let int_min = |signed: bool, int_width: u64| -> i128 { - if signed { i128::MIN >> (128 - int_width) } else { 0 } - }; - - let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) { - let rounded_min = ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero); - assert_eq!(rounded_min.status, Status::OK); - let rounded_max = ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero); - assert!(rounded_max.value.is_finite()); - (rounded_min.value.to_bits(), rounded_max.value.to_bits()) - }; - let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) { - let rounded_min = ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero); - assert_eq!(rounded_min.status, Status::OK); - let rounded_max = ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero); - assert!(rounded_max.value.is_finite()); - (rounded_min.value.to_bits(), rounded_max.value.to_bits()) - }; - - let mut float_bits_to_llval = |bits| { - let bits_llval = match float_width { - 32 => bx.cx().const_u32(bits as u32), - 64 => bx.cx().const_u64(bits as u64), - n => bug!("unsupported float width {}", n), - }; - bx.bitcast(bits_llval, float_ty) - }; - let (f_min, f_max) = match float_width { - 32 => compute_clamp_bounds_single(signed, int_width), - 64 => compute_clamp_bounds_double(signed, int_width), - n => bug!("unsupported float width {}", n), - }; - let f_min = float_bits_to_llval(f_min); - let f_max = float_bits_to_llval(f_max); - // To implement saturation, we perform the following steps: - // - // 1. Cast x to an integer with fpto[su]i. This may result in undef. - // 2. Compare x to f_min and f_max, and use the comparison results to select: - // a) int_ty::MIN if x < f_min or x is NaN - // b) int_ty::MAX if x > f_max - // c) the result of fpto[su]i otherwise - // 3. If x is NaN, return 0.0, otherwise return the result of step 2. - // - // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the - // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of - // undef does not introduce any non-determinism either. - // More importantly, the above procedure correctly implements saturating conversion. - // Proof (sketch): - // If x is NaN, 0 is returned by definition. - // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. - // This yields three cases to consider: - // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with - // saturating conversion for inputs in that range. - // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded - // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger - // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX - // is correct. - // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals - // int_ty::MIN and therefore the return value of int_ty::MIN is correct. - // QED. - - let int_max = bx.cx().const_uint_big(int_ty, int_max(signed, int_width)); - let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128); - let zero = bx.cx().const_uint(int_ty, 0); - - // Step 1 ... - let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); - let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); - - // Step 2: We use two comparisons and two selects, with %s1 being the - // result: - // %less_or_nan = fcmp ult %x, %f_min - // %greater = fcmp olt %x, %f_max - // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result - // %s1 = select %greater, int_ty::MAX, %s0 - // Note that %less_or_nan uses an *unordered* comparison. This - // comparison is true if the operands are not comparable (i.e., if x is - // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if - // x is NaN. - // - // Performance note: Unordered comparison can be lowered to a "flipped" - // comparison and a negation, and the negation can be merged into the - // select. Therefore, it not necessarily any more expensive than an - // ordered ("normal") comparison. Whether these optimizations will be - // performed is ultimately up to the backend, but at least x86 does - // perform them. - let s0 = bx.select(less_or_nan, int_min, fptosui_result); - let s1 = bx.select(greater, int_max, s0); - - // Step 3: NaN replacement. - // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. - // Therefore we only need to execute this step for signed integer types. - if signed { - // LLVM has no isNaN predicate, so we use (x == x) instead - let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); - bx.select(cmp, s1, zero) - } else { - s1 - } -} diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 2c96987d33..773dc2adcf 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,9 +1,7 @@ -use rustc_errors::struct_span_err; use rustc_middle::mir; use super::FunctionCx; use super::LocalRef; -use super::OperandValue; use crate::traits::BuilderMethods; use crate::traits::*; @@ -66,51 +64,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } bx } - mir::StatementKind::LlvmInlineAsm(ref asm) => { - let outputs = asm - .outputs - .iter() - .map(|output| self.codegen_place(&mut bx, output.as_ref())) - .collect(); - - let input_vals = asm.inputs.iter().fold( - Vec::with_capacity(asm.inputs.len()), - |mut acc, (span, input)| { - let op = self.codegen_operand(&mut bx, input); - if let OperandValue::Immediate(_) = op.val { - acc.push(op.immediate()); - } else { - struct_span_err!( - bx.sess(), - span.to_owned(), - E0669, - "invalid value for constraint in inline assembly" - ) - .emit(); - } - acc - }, - ); - - if input_vals.len() == asm.inputs.len() { - let res = bx.codegen_llvm_inline_asm( - &asm.asm, - outputs, - input_vals, - statement.source_info.span, - ); - if !res { - struct_span_err!( - bx.sess(), - statement.source_info.span, - E0668, - "malformed inline assembly" - ) - .emit(); - } - } - bx - } mir::StatementKind::Coverage(box ref coverage) => { self.codegen_coverage(&mut bx, coverage.clone(), statement.source_info.scope); bx diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 63cc6faf9e..f31b0ee592 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -74,8 +74,10 @@ const AARCH64_ALLOWED_FEATURES: &[(&str, Option)] = &[ ("ssbs", Some(sym::aarch64_target_feature)), // FEAT_SB ("sb", Some(sym::aarch64_target_feature)), - // FEAT_PAUTH - ("pauth", Some(sym::aarch64_target_feature)), + // FEAT_PAUTH (address authentication) + ("paca", Some(sym::aarch64_target_feature)), + // FEAT_PAUTH (generic authentication) + ("pacg", Some(sym::aarch64_target_feature)), // FEAT_DPB ("dpb", Some(sym::aarch64_target_feature)), // FEAT_DPB2 @@ -137,6 +139,8 @@ const AARCH64_ALLOWED_FEATURES: &[(&str, Option)] = &[ ("v8.7a", Some(sym::aarch64_target_feature)), ]; +const AARCH64_TIED_FEATURES: &[&[&str]] = &[&["paca", "pacg"]]; + const X86_ALLOWED_FEATURES: &[(&str, Option)] = &[ ("adx", Some(sym::adx_target_feature)), ("aes", None), @@ -256,6 +260,13 @@ pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Opt } } +pub fn tied_target_features(sess: &Session) -> &'static [&'static [&'static str]] { + match &*sess.target.arch { + "aarch64" => AARCH64_TIED_FEATURES, + _ => &[], + } +} + pub(crate) fn provide(providers: &mut Providers) { providers.supported_target_features = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 65f3c754d2..11111a7974 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -3,7 +3,6 @@ use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::def_id::DefId; -use rustc_hir::LlvmInlineAsmInner; use rustc_middle::ty::Instance; use rustc_span::Span; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -42,15 +41,6 @@ pub enum GlobalAsmOperandRef { } pub trait AsmBuilderMethods<'tcx>: BackendTypes { - /// Take an inline assembly expression and splat it out via LLVM - fn codegen_llvm_inline_asm( - &mut self, - ia: &LlvmInlineAsmInner, - outputs: Vec>, - inputs: Vec, - span: Span, - ) -> bool; - /// Take an inline assembly expression and splat it out via LLVM fn codegen_inline_asm( &mut self, diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 48d8809585..53fb21b269 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -1,18 +1,21 @@ use super::abi::AbiBuilderMethods; use super::asm::AsmBuilderMethods; +use super::consts::ConstMethods; use super::coverageinfo::CoverageInfoBuilderMethods; use super::debuginfo::DebugInfoBuilderMethods; use super::intrinsic::IntrinsicCallMethods; -use super::type_::ArgAbiMethods; +use super::misc::MiscMethods; +use super::type_::{ArgAbiMethods, BaseTypeMethods}; use super::{HasCodegen, StaticBuilderMethods}; use crate::common::{ - AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, + AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; use crate::MemFlags; +use rustc_apfloat::{ieee, Float, Round, Status}; use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use rustc_middle::ty::Ty; use rustc_span::Span; @@ -202,6 +205,179 @@ pub trait BuilderMethods<'a, 'tcx>: fn intcast(&mut self, val: Self::Value, dest_ty: Self::Type, is_signed: bool) -> Self::Value; fn pointercast(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; + fn cast_float_to_int( + &mut self, + signed: bool, + x: Self::Value, + dest_ty: Self::Type, + ) -> Self::Value { + let in_ty = self.cx().val_ty(x); + let (float_ty, int_ty) = if self.cx().type_kind(dest_ty) == TypeKind::Vector + && self.cx().type_kind(in_ty) == TypeKind::Vector + { + (self.cx().element_type(in_ty), self.cx().element_type(dest_ty)) + } else { + (in_ty, dest_ty) + }; + assert!(matches!(self.cx().type_kind(float_ty), TypeKind::Float | TypeKind::Double)); + assert_eq!(self.cx().type_kind(int_ty), TypeKind::Integer); + + if let Some(false) = self.cx().sess().opts.debugging_opts.saturating_float_casts { + return if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) }; + } + + let try_sat_result = + if signed { self.fptosi_sat(x, dest_ty) } else { self.fptoui_sat(x, dest_ty) }; + if let Some(try_sat_result) = try_sat_result { + return try_sat_result; + } + + let int_width = self.cx().int_width(int_ty); + let float_width = self.cx().float_width(float_ty); + // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the + // destination integer type after rounding towards zero. This `undef` value can cause UB in + // safe code (see issue #10184), so we implement a saturating conversion on top of it: + // Semantically, the mathematical value of the input is rounded towards zero to the next + // mathematical integer, and then the result is clamped into the range of the destination + // integer type. Positive and negative infinity are mapped to the maximum and minimum value of + // the destination integer type. NaN is mapped to 0. + // + // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to + // a value representable in int_ty. + // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. + // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. + // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly + // representable. Note that this only works if float_ty's exponent range is sufficiently large. + // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 + // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. + // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because + // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). + // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. + let int_max = |signed: bool, int_width: u64| -> u128 { + let shift_amount = 128 - int_width; + if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount } + }; + let int_min = |signed: bool, int_width: u64| -> i128 { + if signed { i128::MIN >> (128 - int_width) } else { 0 } + }; + + let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) { + let rounded_min = + ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero); + assert_eq!(rounded_min.status, Status::OK); + let rounded_max = + ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero); + assert!(rounded_max.value.is_finite()); + (rounded_min.value.to_bits(), rounded_max.value.to_bits()) + }; + let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) { + let rounded_min = + ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero); + assert_eq!(rounded_min.status, Status::OK); + let rounded_max = + ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero); + assert!(rounded_max.value.is_finite()); + (rounded_min.value.to_bits(), rounded_max.value.to_bits()) + }; + // To implement saturation, we perform the following steps: + // + // 1. Cast x to an integer with fpto[su]i. This may result in undef. + // 2. Compare x to f_min and f_max, and use the comparison results to select: + // a) int_ty::MIN if x < f_min or x is NaN + // b) int_ty::MAX if x > f_max + // c) the result of fpto[su]i otherwise + // 3. If x is NaN, return 0.0, otherwise return the result of step 2. + // + // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the + // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of + // undef does not introduce any non-determinism either. + // More importantly, the above procedure correctly implements saturating conversion. + // Proof (sketch): + // If x is NaN, 0 is returned by definition. + // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. + // This yields three cases to consider: + // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with + // saturating conversion for inputs in that range. + // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded + // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger + // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX + // is correct. + // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals + // int_ty::MIN and therefore the return value of int_ty::MIN is correct. + // QED. + + let float_bits_to_llval = |bx: &mut Self, bits| { + let bits_llval = match float_width { + 32 => bx.cx().const_u32(bits as u32), + 64 => bx.cx().const_u64(bits as u64), + n => bug!("unsupported float width {}", n), + }; + bx.bitcast(bits_llval, float_ty) + }; + let (f_min, f_max) = match float_width { + 32 => compute_clamp_bounds_single(signed, int_width), + 64 => compute_clamp_bounds_double(signed, int_width), + n => bug!("unsupported float width {}", n), + }; + let f_min = float_bits_to_llval(self, f_min); + let f_max = float_bits_to_llval(self, f_max); + let int_max = self.cx().const_uint_big(int_ty, int_max(signed, int_width)); + let int_min = self.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128); + let zero = self.cx().const_uint(int_ty, 0); + + // If we're working with vectors, constants must be "splatted": the constant is duplicated + // into each lane of the vector. The algorithm stays the same, we are just using the + // same constant across all lanes. + let maybe_splat = |bx: &mut Self, val| { + if bx.cx().type_kind(dest_ty) == TypeKind::Vector { + bx.vector_splat(bx.vector_length(dest_ty), val) + } else { + val + } + }; + let f_min = maybe_splat(self, f_min); + let f_max = maybe_splat(self, f_max); + let int_max = maybe_splat(self, int_max); + let int_min = maybe_splat(self, int_min); + let zero = maybe_splat(self, zero); + + // Step 1 ... + let fptosui_result = if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) }; + let less_or_nan = self.fcmp(RealPredicate::RealULT, x, f_min); + let greater = self.fcmp(RealPredicate::RealOGT, x, f_max); + + // Step 2: We use two comparisons and two selects, with %s1 being the + // result: + // %less_or_nan = fcmp ult %x, %f_min + // %greater = fcmp olt %x, %f_max + // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result + // %s1 = select %greater, int_ty::MAX, %s0 + // Note that %less_or_nan uses an *unordered* comparison. This + // comparison is true if the operands are not comparable (i.e., if x is + // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if + // x is NaN. + // + // Performance note: Unordered comparison can be lowered to a "flipped" + // comparison and a negation, and the negation can be merged into the + // select. Therefore, it not necessarily any more expensive than an + // ordered ("normal") comparison. Whether these optimizations will be + // performed is ultimately up to the backend, but at least x86 does + // perform them. + let s0 = self.select(less_or_nan, int_min, fptosui_result); + let s1 = self.select(greater, int_max, s0); + + // Step 3: NaN replacement. + // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. + // Therefore we only need to execute this step for signed integer types. + if signed { + // LLVM has no isNaN predicate, so we use (x == x) instead + let cmp = self.fcmp(RealPredicate::RealOEQ, x, x); + self.select(cmp, s1, zero) + } else { + s1 + } + } + fn icmp(&mut self, op: IntPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fcmp(&mut self, op: RealPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; @@ -245,29 +421,22 @@ pub trait BuilderMethods<'a, 'tcx>: fn extract_value(&mut self, agg_val: Self::Value, idx: u64) -> Self::Value; fn insert_value(&mut self, agg_val: Self::Value, elt: Self::Value, idx: u64) -> Self::Value; - fn landing_pad( - &mut self, - ty: Self::Type, - pers_fn: Self::Value, - num_clauses: usize, - ) -> Self::Value; - fn set_cleanup(&mut self, landing_pad: Self::Value); - fn resume(&mut self, exn: Self::Value) -> Self::Value; + fn set_personality_fn(&mut self, personality: Self::Value); + + // These are used by everyone except msvc + fn cleanup_landing_pad(&mut self, ty: Self::Type, pers_fn: Self::Value) -> Self::Value; + fn resume(&mut self, exn: Self::Value); + + // These are used only by msvc fn cleanup_pad(&mut self, parent: Option, args: &[Self::Value]) -> Self::Funclet; - fn cleanup_ret( - &mut self, - funclet: &Self::Funclet, - unwind: Option, - ) -> Self::Value; + fn cleanup_ret(&mut self, funclet: &Self::Funclet, unwind: Option); fn catch_pad(&mut self, parent: Self::Value, args: &[Self::Value]) -> Self::Funclet; fn catch_switch( &mut self, parent: Option, unwind: Option, - num_handlers: usize, + handlers: &[Self::BasicBlock], ) -> Self::Value; - fn add_handler(&mut self, catch_switch: Self::Value, handler: Self::BasicBlock); - fn set_personality_fn(&mut self, personality: Self::Value); fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index cbf570dba4..e77201cf0c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -22,7 +22,7 @@ pub trait CoverageInfoMethods<'tcx>: BackendTypes { pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { /// Returns true if the function source hash was added to the coverage map (even if it had - /// already been added, for this instance). Returns false *only* if `-Z instrument-coverage` is + /// already been added, for this instance). Returns false *only* if `-C instrument-coverage` is /// not enabled (a coverage map is not being generated). fn set_function_source_hash( &mut self, @@ -30,7 +30,7 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { function_source_hash: u64, ) -> bool; - /// Returns true if the counter was added to the coverage map; false if `-Z instrument-coverage` + /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage` /// is not enabled (a coverage map is not being generated). fn add_coverage_counter( &mut self, @@ -40,7 +40,7 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { ) -> bool; /// Returns true if the expression was added to the coverage map; false if - /// `-Z instrument-coverage` is not enabled (a coverage map is not being generated). + /// `-C instrument-coverage` is not enabled (a coverage map is not being generated). fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, @@ -51,7 +51,7 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { region: Option, ) -> bool; - /// Returns true if the region was added to the coverage map; false if `-Z instrument-coverage` + /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage` /// is not enabled (a coverage map is not being generated). fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool; } diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 8729802398..89a0f8245e 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -156,9 +156,37 @@ impl<'tcx> ConstEvalErr<'tcx> { } // Add spans for the stacktrace. Don't print a single-line backtrace though. if self.stacktrace.len() > 1 { + // Helper closure to print duplicated lines. + let mut flush_last_line = |last_frame, times| { + if let Some((line, span)) = last_frame { + err.span_label(span, &line); + // Don't print [... additional calls ...] if the number of lines is small + if times < 3 { + for _ in 0..times { + err.span_label(span, &line); + } + } else { + err.span_label( + span, + format!("[... {} additional calls {} ...]", times, &line), + ); + } + } + }; + + let mut last_frame = None; + let mut times = 0; for frame_info in &self.stacktrace { - err.span_label(frame_info.span, frame_info.to_string()); + let frame = (frame_info.to_string(), frame_info.span); + if last_frame.as_ref() == Some(&frame) { + times += 1; + } else { + flush_last_line(last_frame, times); + last_frame = Some(frame); + times = 0; + } } + flush_last_line(last_frame, times); } // Let the caller finish the job. emit(err) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 3ec9f3ca3b..bfb9c40be5 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -6,8 +6,6 @@ use crate::interpret::{ ScalarMaybeUninit, StackPopCleanup, }; -use rustc_errors::ErrorReported; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; @@ -216,7 +214,7 @@ pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { - assert!(key.param_env.constness() == hir::Constness::Const); + assert!(key.param_env.is_const()); // see comment in eval_to_allocation_raw_provider for what we're doing here if key.param_env.reveal() == Reveal::All { let mut key = key; @@ -251,7 +249,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { - assert!(key.param_env.constness() == hir::Constness::Const); + assert!(key.param_env.is_const()); // Because the constant is computed twice (once per value of `Reveal`), we are at risk of // reporting the same error twice here. To resolve this, we check whether we can evaluate the // constant in the more restrictive `Reveal::UserFacing`, which most likely already was @@ -282,25 +280,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>( let cid = key.value; let def = cid.instance.def.with_opt_param(); - - if let Some(def) = def.as_local() { - if tcx.has_typeck_results(def.did) { - if let Some(error_reported) = tcx.typeck_opt_const_arg(def).tainted_by_errors { - return Err(ErrorHandled::Reported(error_reported)); - } - } - if !tcx.is_mir_available(def.did) { - tcx.sess.delay_span_bug( - tcx.def_span(def.did), - &format!("no MIR body is available for {:?}", def.did), - ); - return Err(ErrorHandled::Reported(ErrorReported {})); - } - if let Some(error_reported) = tcx.mir_const_qualif_opt_const_arg(def).error_occured { - return Err(ErrorHandled::Reported(error_reported)); - } - } - let is_static = tcx.is_static(def.did); let mut ecx = InterpCx::new( diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 821b048eb9..05fbbf45d7 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,5 +1,5 @@ use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; @@ -15,7 +15,8 @@ pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } } -pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { +pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let parent_id = tcx.hir().get_parent_node(hir_id); matches!( tcx.hir().get(parent_id), @@ -29,15 +30,15 @@ pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether /// said intrinsic has a `rustc_const_{un,}stable` attribute. fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - - let node = tcx.hir().get(hir_id); + let def_id = def_id.expect_local(); + let node = tcx.hir().get_by_def_id(def_id); if let hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) = node { // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other // foreign items cannot be evaluated at compile-time. + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = tcx.hir().get_foreign_abi(hir_id) { tcx.lookup_const_stability(def_id).is_some() } else { @@ -50,7 +51,7 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { // If the function itself is not annotated with `const`, it may still be a `const fn` // if it resides in a const trait impl. - is_parent_const_impl_raw(tcx, hir_id) + is_parent_const_impl_raw(tcx, def_id) } else { matches!(node, hir::Node::Ctor(_)) } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 30e9cbe440..e157b58405 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -1,3 +1,5 @@ +use rustc_errors::ErrorReported; +use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; use std::borrow::Borrow; @@ -243,6 +245,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ty::InstanceDef::Item(def) => { if ecx.tcx.is_ctfe_mir_available(def.did) { Ok(ecx.tcx.mir_for_ctfe_opt_const_arg(def)) + } else if ecx.tcx.def_kind(def.did) == DefKind::AssocConst { + ecx.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + "This is likely a const item that is missing from its impl", + ); + throw_inval!(AlreadyReported(ErrorReported {})); } else { let path = ecx.tcx.def_path_str(def.did); Err(ConstEvalErrKind::NeedsRfc(format!("calling extern function `{}`", path)) @@ -347,6 +355,33 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, )?; ecx.write_pointer(ptr, dest)?; } + sym::const_deallocate => { + let ptr = ecx.read_pointer(&args[0])?; + let size = ecx.read_scalar(&args[1])?.to_machine_usize(ecx)?; + let align = ecx.read_scalar(&args[2])?.to_machine_usize(ecx)?; + + let size = Size::from_bytes(size); + let align = match Align::from_bytes(align) { + Ok(a) => a, + Err(err) => throw_ub_format!("align has to be a power of 2, {}", err), + }; + + // If an allocation is created in an another const, + // we don't deallocate it. + let (alloc_id, _, _) = ecx.memory.ptr_get_alloc(ptr)?; + let is_allocated_in_another_const = matches!( + ecx.tcx.get_global_alloc(alloc_id), + Some(interpret::GlobalAlloc::Memory(_)) + ); + + if !is_allocated_in_another_const { + ecx.memory.deallocate( + ptr, + Some((size, align)), + interpret::MemoryKind::Machine(MemoryKind::Heap), + )?; + } + } _ => { return Err(ConstEvalErrKind::NeedsRfc(format!( "calling intrinsic `{}`", diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 91b17d1ac1..ba1d5f45bb 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -11,7 +11,8 @@ use rustc_middle::{ use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; use crate::interpret::{ - intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar, + intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MPlaceTy, + MemPlaceMeta, Scalar, }; mod error; @@ -132,49 +133,46 @@ fn const_to_valtree_inner<'tcx>( } } -/// This function uses `unwrap` copiously, because an already validated constant -/// must have valid fields and can thus never fail outside of compiler bugs. However, it is -/// invoked from the pretty printer, where it can receive enums with no variants and e.g. -/// `read_discriminant` needs to be able to handle that. -pub(crate) fn destructure_const<'tcx>( +/// This function should never fail for validated constants. However, it is also invoked from the +/// pretty printer which might attempt to format invalid constants and in that case it might fail. +pub(crate) fn try_destructure_const<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - val: &'tcx ty::Const<'tcx>, -) -> mir::DestructuredConst<'tcx> { + val: ty::Const<'tcx>, +) -> InterpResult<'tcx, mir::DestructuredConst<'tcx>> { trace!("destructure_const: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - let op = ecx.const_to_op(val, None).unwrap(); + let op = ecx.const_to_op(val, None)?; // We go to `usize` as we cannot allocate anything bigger anyway. - let (field_count, variant, down) = match val.ty.kind() { + let (field_count, variant, down) = match val.ty().kind() { ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), - ty::Adt(def, _) if def.variants.is_empty() => { - return mir::DestructuredConst { variant: None, fields: &[] }; - } ty::Adt(def, _) => { - let variant = ecx.read_discriminant(&op).unwrap().1; - let down = ecx.operand_downcast(&op, variant).unwrap(); + let variant = ecx.read_discriminant(&op)?.1; + let down = ecx.operand_downcast(&op, variant)?; (def.variants[variant].fields.len(), Some(variant), down) } ty::Tuple(substs) => (substs.len(), None, op), _ => bug!("cannot destructure constant {:?}", val), }; - let fields_iter = (0..field_count).map(|i| { - let field_op = ecx.operand_field(&down, i).unwrap(); - let val = op_to_const(&ecx, &field_op); - ty::Const::from_value(tcx, val, field_op.layout.ty) - }); - let fields = tcx.arena.alloc_from_iter(fields_iter); + let fields = (0..field_count) + .map(|i| { + let field_op = ecx.operand_field(&down, i)?; + let val = op_to_const(&ecx, &field_op); + Ok(ty::Const::from_value(tcx, val, field_op.layout.ty)) + }) + .collect::>>()?; + let fields = tcx.arena.alloc_from_iter(fields); - mir::DestructuredConst { variant, fields } + Ok(mir::DestructuredConst { variant, fields }) } pub(crate) fn deref_const<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - val: &'tcx ty::Const<'tcx>, -) -> &'tcx ty::Const<'tcx> { + val: ty::Const<'tcx>, +) -> ty::Const<'tcx> { trace!("deref_const: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); let op = ecx.const_to_op(val, None).unwrap(); @@ -194,7 +192,7 @@ pub(crate) fn deref_const<'tcx>( // In case of unsized types, figure out the real type behind. MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() { ty::Str => bug!("there's no sized equivalent of a `str`"), - ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()), + ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_machine_usize(&tcx).unwrap()), _ => bug!( "type {} should not have metadata, but had {:?}", mplace.layout.ty, @@ -203,5 +201,5 @@ pub(crate) fn deref_const<'tcx>( }, }; - tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty }) + tcx.mk_const(ty::ConstS { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty }) } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 4c4b0bd2d1..e2c4eb1dad 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -315,7 +315,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { - self.unsize_into_ptr(src, dest, s, c) + self.unsize_into_ptr(src, dest, *s, *c) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 0a8112da2a..1b86bcfa8c 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -509,20 +509,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { instance: ty::InstanceDef<'tcx>, promoted: Option, ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { - // do not continue if typeck errors occurred (can only occur in local crate) let def = instance.with_opt_param(); - if let Some(def) = def.as_local() { - if self.tcx.has_typeck_results(def.did) { - if let Some(error_reported) = self.tcx.typeck_opt_const_arg(def).tainted_by_errors { - throw_inval!(AlreadyReported(error_reported)) - } - } - } trace!("load mir(instance={:?}, promoted={:?})", instance, promoted); - if let Some(promoted) = promoted { - return Ok(&self.tcx.promoted_mir_opt_const_arg(def)[promoted]); + let body = if let Some(promoted) = promoted { + &self.tcx.promoted_mir_opt_const_arg(def)[promoted] + } else { + M::load_mir(self, instance)? + }; + // do not continue if typeck errors occurred (can only occur in local crate) + if let Some(err) = body.tainted_by_errors { + throw_inval!(AlreadyReported(err)); } - M::load_mir(self, instance) + Ok(body) } /// Call this on things you got out of the MIR (so it is as generic as the current diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs index ca000f93eb..e6f243e28d 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/type_name.rs @@ -68,7 +68,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { } } - fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result { + fn print_const(self, ct: ty::Const<'tcx>) -> Result { self.pretty_print_const(ct, false) } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index dfec450968..ec5eafcd63 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -561,20 +561,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// "universe" (param_env). pub fn const_to_op( &self, - val: &ty::Const<'tcx>, + val: ty::Const<'tcx>, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - match val.val { + match val.val() { ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric), ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)), ty::ConstKind::Unevaluated(uv) => { - let instance = self.resolve(uv.def, uv.substs(*self.tcx))?; + let instance = self.resolve(uv.def, uv.substs)?; Ok(self.eval_to_allocation(GlobalId { instance, promoted: uv.promoted })?.into()) } ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => { span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val) } - ty::ConstKind::Value(val_val) => self.const_val_to_op(val_val, val.ty, layout), + ty::ConstKind::Value(val_val) => self.const_val_to_op(val_val, val.ty(), layout), } } @@ -584,8 +584,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { match val { - mir::ConstantKind::Ty(ct) => self.const_to_op(ct, layout), - mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, ty, layout), + mir::ConstantKind::Ty(ct) => self.const_to_op(*ct, layout), + mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, *ty, layout), } } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 818b95b7fc..7b06ffaf15 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -462,7 +462,7 @@ where let (meta, ty) = match base.layout.ty.kind() { // It is not nice to match on the type, but that seems to be the only way to // implement this. - ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(inner, inner_len)), + ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)), ty::Slice(..) => { let len = Scalar::from_machine_usize(inner_len, self); (MemPlaceMeta::Meta(len), base.layout.ty) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 3daa1d3c2b..57ba9b4099 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -140,8 +140,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. Nop => {} - - LlvmInlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"), } self.stack_mut()[frame_idx].loc.as_mut().unwrap().statement_index += 1; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index a16388d5de..e17bd9a8c0 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -3,13 +3,17 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use std::convert::TryInto; use std::ops::ControlFlow; -/// Returns `true` if a used generic parameter requires substitution. +/// Checks whether a type contains generic parameters which require substitution. +/// +/// In case it does, returns a `TooGeneric` const eval error. Note that due to polymorphization +/// types may be "concrete enough" even though they still contain generic parameters in +/// case these parameters are unused. crate fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> where T: TypeFoldable<'tcx>, { debug!("ensure_monomorphic_enough: ty={:?}", ty); - if !ty.potentially_needs_subst() { + if !ty.needs_subst() { return Ok(()); } @@ -21,12 +25,8 @@ where impl<'tcx> TypeVisitor<'tcx> for UsedParamsNeedSubstVisitor<'tcx> { type BreakTy = FoundParam; - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - if !ty.potentially_needs_subst() { + if !ty.needs_subst() { return ControlFlow::CONTINUE; } @@ -44,7 +44,7 @@ where 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.definitely_needs_subst(self.tcx)) { + match (is_used, subst.needs_subst()) { // Just in case there are closures or generators within this subst, // recurse. (true, true) => return subst.super_visit_with(self), @@ -55,7 +55,7 @@ where assert!(matches!(ty.kind(), ty::Param(_))) } ty::subst::GenericArgKind::Const(ct) => { - assert!(matches!(ct.val, ty::ConstKind::Param(_))) + assert!(matches!(ct.val(), ty::ConstKind::Param(_))) } ty::subst::GenericArgKind::Lifetime(..) => (), }, @@ -68,8 +68,8 @@ where } } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { - match c.val { + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + match c.val() { ty::ConstKind::Param(..) => ControlFlow::Break(FoundParam), _ => c.super_visit_with(self), } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 5a398c2f45..4060bee7e0 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -267,14 +267,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' match layout.variants { Variants::Single { index } => { // Inside a variant - PathElem::Field(def.variants[index].fields[field].ident.name) + PathElem::Field(def.variants[index].fields[field].name) } Variants::Multiple { .. } => bug!("we handled variants above"), } } // other ADTs - ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].ident.name), + ty::Adt(def, _) => PathElem::Field(def.non_enum_variant().fields[field].name), // arrays/slices ty::Array(..) | ty::Slice(..) => PathElem::ArrayElem(field), @@ -553,7 +553,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' { // A mutable reference inside a const? That does not seem right (except if it is // a ZST). - let layout = self.ecx.layout_of(ty)?; + let layout = self.ecx.layout_of(*ty)?; if !layout.is_zst() { throw_validation_failure!(self.path, { "mutable reference in a `const`" }); } @@ -726,7 +726,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> new_op: &OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { let name = match old_op.layout.ty.kind() { - ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].ident.name), + ty::Adt(adt, _) => PathElem::Variant(adt.variants[variant_id].name), // Generators also have variants ty::Generator(..) => PathElem::GeneratorState(variant_id), _ => bug!("Unexpected type with variant: {:?}", old_op.layout.ty), @@ -837,7 +837,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // This is the length of the array/slice. let len = mplace.len(self.ecx)?; // This is the element type size. - let layout = self.ecx.layout_of(tys)?; + let layout = self.ecx.layout_of(*tys)?; // This is the size in bytes of the whole array. (This checks for overflow.) let size = layout.size * len; @@ -896,7 +896,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // Fast path for arrays and slices of ZSTs. We only need to check a single ZST element // of an array and not all of them, because there's only a single value of a specific // ZST type, so either validation fails for all elements or none. - ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(tys)?.is_zst() => { + ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => { // Validate just the first element (if any). self.walk_aggregate(op, fields.take(1))? } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 92854af55b..77d312f585 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -22,6 +22,7 @@ Rust MIR: a lowered representation of Rust. #![feature(trusted_step)] #![feature(try_blocks)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; @@ -40,9 +41,9 @@ 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.destructure_const = |tcx, param_env_and_value| { + providers.try_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) + const_eval::try_destructure_const(tcx, param_env, value).ok() }; providers.const_to_valtree = |tcx, param_env_and_value| { let (param_env, raw) = param_env_and_value.into_parts(); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index dd749c0393..095c8f84f4 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, use rustc_middle::ty::{Binder, TraitPredicate, TraitRef}; use rustc_mir_dataflow::{self, Analysis}; use rustc_span::{sym, Span, Symbol}; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::SelectionContext; use std::mem; @@ -120,7 +121,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { fn in_return_place( &mut self, ccx: &'mir ConstCx<'mir, 'tcx>, - error_occured: Option, + tainted_by_errors: Option, ) -> ConstQualifs { // Find the `Return` terminator if one exists. // @@ -134,7 +135,9 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { .map(|(bb, _)| bb); let return_block = match return_block { - None => return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty(), error_occured), + None => { + return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty(), tainted_by_errors); + } Some(bb) => bb, }; @@ -166,7 +169,7 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc), has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc), custom_eq, - error_occured, + tainted_by_errors, } } } @@ -221,8 +224,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { // Prevent const trait methods from being annotated as `stable`. // FIXME: Do this as part of stability checking. if self.is_const_stable_const_fn() { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) { + if crate::const_eval::is_parent_const_impl_raw(tcx, def_id) { self.ccx .tcx .sess @@ -292,13 +294,13 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { } /// Emits an error if an expression cannot be evaluated in the current context. - pub fn check_op(&mut self, op: impl NonConstOp) { + pub fn check_op(&mut self, op: impl NonConstOp<'tcx>) { self.check_op_spanned(op, self.span); } /// Emits an error at the given `span` if an expression cannot be evaluated in the current /// context. - pub fn check_op_spanned(&mut self, op: O, span: Span) { + pub fn check_op_spanned>(&mut self, op: O, span: Span) { let gate = match op.status_in_item(self.ccx) { Status::Allowed => return, @@ -348,7 +350,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>, local: Local) { let kind = self.body.local_kind(local); - for ty in ty.walk(self.tcx) { + for ty in ty.walk() { let ty = match ty.unpack() { GenericArgKind::Type(ty) => ty, @@ -752,10 +754,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.super_statement(statement, location); match statement.kind { - StatementKind::LlvmInlineAsm { .. } => { - self.check_op(ops::InlineAsm); - } - StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } | StatementKind::FakeRead(..) @@ -776,7 +774,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.super_terminator(terminator, location); match &terminator.kind { - TerminatorKind::Call { func, args, .. } => { + TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => { let ConstCx { tcx, body, param_env, .. } = *self.ccx; let caller = self.def_id().to_def_id(); @@ -800,20 +798,24 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { 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(Some((callee, substs)))); + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } let trait_ref = TraitRef::from_method(tcx, trait_id, substs); - let obligation = Obligation::new( - ObligationCause::dummy(), - param_env, - Binder::dummy(TraitPredicate { - trait_ref, - constness: ty::BoundConstness::ConstIfConst, - polarity: ty::ImplPolarity::Positive, - }), - ); + let poly_trait_pred = Binder::dummy(TraitPredicate { + trait_ref, + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + }); + let obligation = + Obligation::new(ObligationCause::dummy(), param_env, poly_trait_pred); let implsrc = tcx.infer_ctxt().enter(|infcx| { let mut selcx = SelectionContext::new(&infcx); @@ -840,22 +842,61 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { substs = InternalSubsts::identity_for_item(tcx, did); callee = did; } + + if let hir::Constness::NotConst = tcx.impl_constness(data.impl_def_id) { + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); + return; + } } _ if !tcx.is_const_fn_raw(callee) => { // At this point, it is only legal when the caller is marked with // #[default_method_body_is_const], and the callee is in the same // trait. let callee_trait = tcx.trait_of_item(callee); - if callee_trait.is_some() { - if tcx.has_attr(caller, sym::default_method_body_is_const) { - if tcx.trait_of_item(caller) == callee_trait { - nonconst_call_permission = true; - } - } + if callee_trait.is_some() + && tcx.has_attr(caller, sym::default_method_body_is_const) + && callee_trait == tcx.trait_of_item(caller) + // Can only call methods when it's `::f`. + && tcx.types.self_param == substs.type_at(0) + { + nonconst_call_permission = true; } if !nonconst_call_permission { - self.check_op(ops::FnCallNonConst(None)); + let obligation = Obligation::new( + ObligationCause::dummy_with_span(*fn_span), + param_env, + tcx.mk_predicate( + poly_trait_pred.map_bound(ty::PredicateKind::Trait), + ), + ); + + // improve diagnostics by showing what failed. Our requirements are stricter this time + // as we are going to error again anyways. + tcx.infer_ctxt().enter(|infcx| { + if let Err(e) = implsrc { + infcx.report_selection_error( + obligation.clone(), + &obligation, + &e, + false, + ); + } + }); + + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } } @@ -924,7 +965,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } if !nonconst_call_permission { - self.check_op(ops::FnCallNonConst(None)); + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 24c4a4915e..8c3f8e8816 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -3,14 +3,22 @@ use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; +use rustc_middle::mir; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::{mir, ty::AssocKind}; +use rustc_middle::ty::{ + suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, TraitPredicate, Ty, +}; +use rustc_middle::ty::{Binder, BoundConstness, ImplPolarity, TraitRef}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; -use rustc_span::{symbol::Ident, Span, Symbol}; -use rustc_span::{BytePos, Pos}; +use rustc_span::{BytePos, Pos, Span, Symbol}; +use rustc_trait_selection::traits::SelectionContext; use super::ConstCx; +use crate::util::{call_kind, CallDesugaringKind, CallKind}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Status { @@ -29,9 +37,9 @@ pub enum DiagnosticImportance { } /// An operation that is not *always* allowed in a const context. -pub trait NonConstOp: std::fmt::Debug { +pub trait NonConstOp<'tcx>: std::fmt::Debug { /// Returns an enum indicating whether this operation is allowed within the given item. - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Forbidden } @@ -39,13 +47,13 @@ pub trait NonConstOp: std::fmt::Debug { DiagnosticImportance::Primary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>; + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>; } #[derive(Debug)] pub struct FloatingPointOp; -impl NonConstOp for FloatingPointOp { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for FloatingPointOp { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() == hir::ConstContext::ConstFn { Status::Unstable(sym::const_fn_floating_point_arithmetic) } else { @@ -53,7 +61,7 @@ impl NonConstOp for FloatingPointOp { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_floating_point_arithmetic, @@ -66,77 +74,229 @@ impl NonConstOp for FloatingPointOp { /// A function call where the callee is a pointer. #[derive(Debug)] pub struct FnCallIndirect; -impl NonConstOp for FnCallIndirect { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn") } } /// A function call where the callee is not marked as `const`. -#[derive(Debug)] -pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>); -impl<'a> NonConstOp for FnCallNonConst<'a> { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - let mut err = struct_span_err!( - ccx.tcx.sess, - span, - E0015, - "calls in {}s are limited to constant functions, \ - tuple structs and tuple variants", - ccx.const_kind(), - ); +#[derive(Debug, Clone, Copy)] +pub struct FnCallNonConst<'tcx> { + pub caller: DefId, + pub callee: DefId, + pub substs: SubstsRef<'tcx>, + pub span: Span, + pub from_hir_call: bool, +} - if let FnCallNonConst(Some((callee, substs))) = *self { - if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() { - if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind( - ccx.tcx, - Ident::with_dummy_span(sym::eq), - AssocKind::Fn, - trait_def_id, - ) { - if callee == eq_item.def_id && substs.len() == 2 { - match (substs[0].unpack(), substs[1].unpack()) { - (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) - if self_ty == rhs_ty - && self_ty.is_ref() - && self_ty.peel_refs().is_primitive() => - { - let mut num_refs = 0; - let mut tmp_ty = self_ty; - while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { - num_refs += 1; - tmp_ty = inner_ty; - } - let deref = "*".repeat(num_refs); - - if let Ok(call_str) = - ccx.tcx.sess.source_map().span_to_snippet(span) - { - if let Some(eq_idx) = call_str.find("==") { - if let Some(rhs_idx) = call_str[(eq_idx + 2)..] - .find(|c: char| !c.is_whitespace()) - { - let rhs_pos = span.lo() - + BytePos::from_usize(eq_idx + 2 + rhs_idx); - let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); - err.multipart_suggestion( - "consider dereferencing here", - vec![ - (span.shrink_to_lo(), deref.clone()), - (rhs_span, deref), - ], - Applicability::MachineApplicable, - ); - } +impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> { + let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self; + let ConstCx { tcx, param_env, .. } = *ccx; + + let diag_trait = |mut err, self_ty: Ty<'_>, trait_id| { + let trait_ref = TraitRef::from_method(tcx, trait_id, substs); + + match self_ty.kind() { + Param(param_ty) => { + debug!(?param_ty); + if let Some(generics) = caller + .as_local() + .map(|id| tcx.hir().local_def_id_to_hir_id(id)) + .map(|id| tcx.hir().get(id)) + .as_ref() + .and_then(|node| node.generics()) + { + let constraint = with_no_trimmed_paths(|| { + format!("~const {}", trait_ref.print_only_trait_path()) + }); + suggest_constraining_type_param( + tcx, + generics, + &mut err, + ¶m_ty.name.as_str(), + &constraint, + None, + ); + } + } + Adt(..) => { + let obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + Binder::dummy(TraitPredicate { + trait_ref, + constness: BoundConstness::NotConst, + polarity: ImplPolarity::Positive, + }), + ); + + let implsrc = tcx.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + selcx.select(&obligation) + }); + + if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { + let span = + tcx.sess.source_map().guess_head_span(tcx.def_span(data.impl_def_id)); + err.span_note(span, "impl defined here, but it is not `const`"); + } + } + _ => {} + } + + err + }; + + let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None); + + debug!(?call_kind); + + let mut err = match call_kind { + CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { + macro_rules! error { + ($fmt:literal) => { + struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind()) + }; + } + + let err = match kind { + CallDesugaringKind::ForLoopIntoIter => { + error!("cannot convert `{}` into an iterator in {}s") + } + CallDesugaringKind::QuestionBranch => { + error!("`?` cannot determine the branch of `{}` in {}s") + } + CallDesugaringKind::QuestionFromResidual => { + error!("`?` cannot convert from residual of `{}` in {}s") + } + CallDesugaringKind::TryBlockFromOutput => { + error!("`try` block cannot convert `{}` to the result in {}s") + } + }; + + diag_trait(err, self_ty, kind.trait_def_id(tcx)) + } + CallKind::FnCall { fn_trait_id, self_ty } => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0015, + "cannot call non-const closure in {}s", + ccx.const_kind(), + ); + + match self_ty.kind() { + FnDef(def_id, ..) => { + let span = tcx.sess.source_map().guess_head_span(tcx.def_span(*def_id)); + if ccx.tcx.is_const_fn_raw(*def_id) { + span_bug!(span, "calling const FnDef errored when it shouldn't"); + } + + err.span_note(span, "function defined here, but it is not `const`"); + } + FnPtr(..) => { + err.note(&format!( + "function pointers need an RFC before allowed to be called in {}s", + ccx.const_kind() + )); + } + Closure(..) => { + err.note(&format!( + "closures need an RFC before allowed to be called in {}s", + ccx.const_kind() + )); + } + _ => {} + } + + diag_trait(err, self_ty, fn_trait_id) + } + CallKind::Operator { trait_id, self_ty, .. } => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0015, + "cannot call non-const operator in {}s", + ccx.const_kind() + ); + + if Some(trait_id) == ccx.tcx.lang_items().eq_trait() { + match (substs[0].unpack(), substs[1].unpack()) { + (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) + if self_ty == rhs_ty + && self_ty.is_ref() + && self_ty.peel_refs().is_primitive() => + { + let mut num_refs = 0; + let mut tmp_ty = self_ty; + while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { + num_refs += 1; + tmp_ty = *inner_ty; + } + let deref = "*".repeat(num_refs); + + if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) { + if let Some(eq_idx) = call_str.find("==") { + if let Some(rhs_idx) = + call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) + { + let rhs_pos = + span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); + let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); + err.multipart_suggestion( + "consider dereferencing here", + vec![ + (span.shrink_to_lo(), deref.clone()), + (rhs_span, deref), + ], + Applicability::MachineApplicable, + ); } } } - _ => {} } + _ => {} } } + + diag_trait(err, self_ty, trait_id) } - } + CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0015, + "cannot perform deref coercion on `{}` in {}s", + self_ty, + ccx.const_kind() + ); + + err.note(&format!("attempting to deref into `{}`", deref_target_ty)); + + // Check first whether the source is accessible (issue #87060) + if tcx.sess.source_map().span_to_snippet(deref_target).is_ok() { + err.span_note(deref_target, "deref defined here"); + } + + diag_trait(err, self_ty, tcx.lang_items().deref_trait().unwrap()) + } + _ => struct_span_err!( + ccx.tcx.sess, + span, + E0015, + "cannot call non-const fn `{}` in {}s", + ccx.tcx.def_path_str_with_substs(callee, substs), + ccx.const_kind(), + ), + }; + + err.note(&format!( + "calls in {}s are limited to constant functions, \ + tuple structs and tuple variants", + ccx.const_kind(), + )); err } @@ -148,8 +308,8 @@ impl<'a> NonConstOp for FnCallNonConst<'a> { #[derive(Debug)] pub struct FnCallUnstable(pub DefId, pub Option); -impl NonConstOp for FnCallUnstable { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let FnCallUnstable(def_id, feature) = *self; let mut err = ccx.tcx.sess.struct_span_err( @@ -174,8 +334,8 @@ impl NonConstOp for FnCallUnstable { #[derive(Debug)] pub struct FnPtrCast; -impl NonConstOp for FnPtrCast { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for FnPtrCast { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -183,7 +343,7 @@ impl NonConstOp for FnPtrCast { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_fn_ptr_basics, @@ -195,8 +355,8 @@ impl NonConstOp for FnPtrCast { #[derive(Debug)] pub struct Generator(pub hir::GeneratorKind); -impl NonConstOp for Generator { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for Generator { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 { Status::Unstable(sym::const_async_blocks) } else { @@ -204,7 +364,7 @@ impl NonConstOp for Generator { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind()); if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 { feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg) @@ -216,8 +376,8 @@ impl NonConstOp for Generator { #[derive(Debug)] pub struct HeapAllocation; -impl NonConstOp for HeapAllocation { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for HeapAllocation { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -240,8 +400,8 @@ impl NonConstOp for HeapAllocation { #[derive(Debug)] pub struct InlineAsm; -impl NonConstOp for InlineAsm { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for InlineAsm { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { struct_span_err!( ccx.tcx.sess, span, @@ -256,8 +416,8 @@ impl NonConstOp for InlineAsm { pub struct LiveDrop { pub dropped_at: Option, } -impl NonConstOp for LiveDrop { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for LiveDrop { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -276,8 +436,8 @@ impl NonConstOp for LiveDrop { /// 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 { +impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_refs_to_cell) } fn importance(&self) -> DiagnosticImportance { @@ -285,7 +445,7 @@ impl NonConstOp for TransientCellBorrow { // not additionally emit a feature gate error if activating the feature gate won't work. DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_refs_to_cell, @@ -300,8 +460,8 @@ impl NonConstOp for TransientCellBorrow { /// 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<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for CellBorrow { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -337,8 +497,8 @@ impl NonConstOp for CellBorrow { /// static or const items. pub struct MutBorrow(pub hir::BorrowKind); -impl NonConstOp for MutBorrow { - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for MutBorrow { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Forbidden } @@ -348,7 +508,7 @@ impl NonConstOp for MutBorrow { DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let raw = match self.0 { hir::BorrowKind::Raw => "raw ", hir::BorrowKind::Ref => "", @@ -382,12 +542,12 @@ impl NonConstOp for MutBorrow { #[derive(Debug)] pub struct TransientMutBorrow(pub hir::BorrowKind); -impl NonConstOp for TransientMutBorrow { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let raw = match self.0 { hir::BorrowKind::Raw => "raw ", hir::BorrowKind::Ref => "", @@ -404,8 +564,8 @@ impl NonConstOp for TransientMutBorrow { #[derive(Debug)] pub struct MutDeref; -impl NonConstOp for MutDeref { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for MutDeref { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } @@ -414,7 +574,7 @@ impl NonConstOp for MutDeref { DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -427,8 +587,8 @@ impl NonConstOp for MutDeref { /// A call to a `panic()` lang item where the first argument is _not_ a `&str`. #[derive(Debug)] pub struct PanicNonStr; -impl NonConstOp for PanicNonStr { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for PanicNonStr { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { ccx.tcx.sess.struct_span_err( span, "argument to `panic!()` in a const context must have type `&str`", @@ -441,8 +601,8 @@ impl NonConstOp for PanicNonStr { /// allocation base addresses that are not known at compile-time. #[derive(Debug)] pub struct RawPtrComparison; -impl NonConstOp for RawPtrComparison { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = ccx .tcx .sess @@ -457,12 +617,12 @@ impl NonConstOp for RawPtrComparison { #[derive(Debug)] pub struct RawMutPtrDeref; -impl NonConstOp for RawMutPtrDeref { +impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { Status::Unstable(sym::const_mut_refs) } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -477,8 +637,8 @@ impl NonConstOp for RawMutPtrDeref { /// allocation base addresses that are not known at compile-time. #[derive(Debug)] pub struct RawPtrToIntCast; -impl NonConstOp for RawPtrToIntCast { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = ccx .tcx .sess @@ -494,8 +654,8 @@ impl NonConstOp for RawPtrToIntCast { /// An access to a (non-thread-local) `static`. #[derive(Debug)] pub struct StaticAccess; -impl NonConstOp for StaticAccess { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for StaticAccess { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if let hir::ConstContext::Static(_) = ccx.const_kind() { Status::Allowed } else { @@ -503,7 +663,7 @@ impl NonConstOp for StaticAccess { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -528,8 +688,8 @@ impl NonConstOp for StaticAccess { /// An access to a thread-local `static`. #[derive(Debug)] pub struct ThreadLocalAccess; -impl NonConstOp for ThreadLocalAccess { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { struct_span_err!( ccx.tcx.sess, span, @@ -546,8 +706,8 @@ pub mod ty { #[derive(Debug)] pub struct MutRef(pub mir::LocalKind); - impl NonConstOp for MutRef { - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + impl<'tcx> NonConstOp<'tcx> for MutRef { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } @@ -560,11 +720,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -576,7 +732,7 @@ pub mod ty { #[derive(Debug)] pub struct FnPtr(pub mir::LocalKind); - impl NonConstOp for FnPtr { + impl<'tcx> NonConstOp<'tcx> for FnPtr { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -586,7 +742,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -594,11 +750,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_fn_ptr_basics, @@ -610,16 +762,12 @@ pub mod ty { #[derive(Debug)] pub struct ImplTrait; - impl NonConstOp for ImplTrait { + impl<'tcx> NonConstOp<'tcx> for ImplTrait { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { Status::Unstable(sym::const_impl_trait) } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_impl_trait, @@ -631,7 +779,7 @@ pub mod ty { #[derive(Debug)] pub struct TraitBound(pub mir::LocalKind); - impl NonConstOp for TraitBound { + impl<'tcx> NonConstOp<'tcx> for TraitBound { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -641,7 +789,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -649,11 +797,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_trait_bound, @@ -674,7 +818,7 @@ pub mod ty { #[derive(Debug)] pub struct DynTrait(pub mir::LocalKind); - impl NonConstOp for DynTrait { + impl<'tcx> NonConstOp<'tcx> for DynTrait { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -684,7 +828,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -692,11 +836,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_trait_bound, @@ -718,16 +858,12 @@ pub mod ty { /// A trait bound with the `?const Trait` opt-out #[derive(Debug)] pub struct TraitBoundNotConst; - impl NonConstOp for TraitBoundNotConst { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + impl<'tcx> NonConstOp<'tcx> for TraitBoundNotConst { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_trait_bound_opt_out) } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_trait_bound_opt_out, diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 27f2da3426..639b798be5 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -4,11 +4,12 @@ use rustc_errors::ErrorReported; use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::TraitEngine; use rustc_middle::mir::*; use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::{ - self, ImplSource, Obligation, ObligationCause, SelectionContext, + self, FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, }; use super::ConstCx; @@ -16,14 +17,14 @@ use super::ConstCx; pub fn in_any_value_of_ty<'tcx>( cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>, - error_occured: Option, + tainted_by_errors: Option, ) -> ConstQualifs { ConstQualifs { has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty), custom_eq: CustomEq::in_any_value_of_ty(cx, ty), - error_occured, + tainted_by_errors, } } @@ -145,15 +146,10 @@ impl Qualif for NeedsNonConstDrop { qualifs.needs_non_const_drop } - fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, mut ty: Ty<'tcx>) -> bool { - // Avoid selecting for simple cases. - match ty::util::needs_drop_components(ty, &cx.tcx.data_layout).as_deref() { - Ok([]) => return false, - Err(ty::util::AlwaysRequiresDrop) => return true, - // If we've got a single component, select with that - // to increase the chance that we hit the selection cache. - Ok([t]) => ty = t, - Ok([..]) => {} + fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { + // Avoid selecting for simple cases, such as builtin types. + if ty::util::is_trivially_const_drop(ty) { + return false; } let Some(drop_trait) = cx.tcx.lang_items().drop_trait() else { @@ -161,28 +157,50 @@ impl Qualif for NeedsNonConstDrop { // without having the lang item present. return false; }; - let trait_ref = - ty::TraitRef { def_id: drop_trait, substs: cx.tcx.mk_substs_trait(ty, &[]) }; + let obligation = Obligation::new( ObligationCause::dummy(), cx.param_env, ty::Binder::dummy(ty::TraitPredicate { - trait_ref, + trait_ref: ty::TraitRef { + def_id: drop_trait, + substs: cx.tcx.mk_substs_trait(ty, &[]), + }, constness: ty::BoundConstness::ConstIfConst, polarity: ty::ImplPolarity::Positive, }), ); - let implsrc = cx.tcx.infer_ctxt().enter(|infcx| { + cx.tcx.infer_ctxt().enter(|infcx| { let mut selcx = SelectionContext::new(&infcx); - selcx.select(&obligation) - }); - !matches!( - implsrc, - Ok(Some( + let Some(impl_src) = selcx.select(&obligation).ok().flatten() else { + // If we couldn't select a const drop candidate, then it's bad + return true; + }; + + if !matches!( + impl_src, ImplSource::ConstDrop(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) - )) - ) + ) { + // If our const drop candidate is not ConstDrop or implied by the param env, + // then it's bad + return true; + } + + if impl_src.borrow_nested_obligations().is_empty() { + return false; + } + + // If we successfully found one, then select all of the predicates + // implied by our const drop impl. + let mut fcx = FulfillmentContext::new(); + for nested in impl_src.nested_obligations() { + fcx.register_predicate_obligation(&infcx, nested); + } + + // If we had any errors, then it's bad + !fcx.select_all_or_error(&infcx).is_empty() + }) } fn in_adt_inherently<'tcx>( @@ -210,8 +228,7 @@ impl Qualif for CustomEq { // because that component may be part of an enum variant (e.g., // `Option::::Some`), in which case some values of this type may be // structural-match (`Option::None`). - let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id()); - traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).is_some() + traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some() } fn in_adt_inherently<'tcx>( @@ -338,7 +355,7 @@ where // Check the qualifs of the value of `const` items. if let Some(ct) = constant.literal.const_for_ty() { - if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted }) = ct.val { + if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) = ct.val() { // Use qualifs of the type for the promoted. Promoteds in MIR body should be possible // only for `NeedsNonConstDrop` with precise drop checking. This is the only const // check performed after the promotion. Verify that with an assertion. diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 55fba5d7dd..cacc0018fe 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -496,7 +496,7 @@ impl<'tcx> Validator<'_, 'tcx> { 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"); + 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); @@ -839,21 +839,17 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { span, user_ty: None, literal: tcx - .mk_const(ty::Const { + .mk_const(ty::ConstS { ty, val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, - substs_: Some(InternalSubsts::for_item( - tcx, - def.did, - |param, _| { - if let ty::GenericParamDefKind::Lifetime = param.kind { - tcx.lifetimes.re_erased.into() - } else { - tcx.mk_param_from_def(param) - } - }, - )), + substs: InternalSubsts::for_item(tcx, def.did, |param, _| { + if let ty::GenericParamDefKind::Lifetime = param.kind { + tcx.lifetimes.re_erased.into() + } else { + tcx.mk_param_from_def(param) + } + }), promoted: Some(promoted_id), }), }) @@ -969,7 +965,6 @@ pub fn promote_candidates<'tcx>( scope.parent_scope = None; let promoted = Body::new( - tcx, body.source, // `promoted` gets filled in below IndexVec::new(), IndexVec::from_elem_n(scope, 1), @@ -979,6 +974,7 @@ pub fn promote_candidates<'tcx>( vec![], body.span, body.generator_kind(), + body.tainted_by_errors, ); let promoter = Promoter { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index c3f81a3ab8..cf15fc4ddc 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -55,6 +55,7 @@ impl<'tcx> MirPass<'tcx> for Validator { reachable_blocks: traversal::reachable_as_bitset(body), storage_liveness, place_cache: Vec::new(), + value_cache: Vec::new(), } .visit_body(body); } @@ -109,6 +110,7 @@ struct TypeChecker<'a, 'tcx> { reachable_blocks: BitSet, storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive>, place_cache: Vec>, + value_cache: Vec, } impl<'a, 'tcx> TypeChecker<'a, 'tcx> { @@ -352,7 +354,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { StatementKind::SetDiscriminant { .. } | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - | StatementKind::LlvmInlineAsm(..) | StatementKind::Retag(_, _) | StatementKind::Coverage(_) | StatementKind::Nop => {} @@ -399,6 +400,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.check_edge(location, target, EdgeKind::Normal); } self.check_edge(location, targets.otherwise(), EdgeKind::Normal); + + self.value_cache.clear(); + self.value_cache.extend(targets.iter().map(|(value, _)| value)); + let all_len = self.value_cache.len(); + self.value_cache.sort_unstable(); + self.value_cache.dedup(); + let has_duplicates = all_len != self.value_cache.len(); + if has_duplicates { + self.fail( + location, + format!( + "duplicated values in `SwitchInt` terminator: {:?}", + terminator.kind, + ), + ); + } } TerminatorKind::Drop { target, unwind, .. } => { self.check_edge(location, *target, EdgeKind::Normal); diff --git a/compiler/rustc_const_eval/src/util/call_kind.rs b/compiler/rustc_const_eval/src/util/call_kind.rs new file mode 100644 index 0000000000..11bb9508a1 --- /dev/null +++ b/compiler/rustc_const_eval/src/util/call_kind.rs @@ -0,0 +1,143 @@ +//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`, +//! as well as errors when attempting to call a non-const function in a const +//! context. + +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItemGroup; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, AssocItemContainer, DefIdTree, Instance, ParamEnv, Ty, TyCtxt}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, DesugaringKind, Span}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CallDesugaringKind { + /// for _ in x {} calls x.into_iter() + ForLoopIntoIter, + /// x? calls x.branch() + QuestionBranch, + /// x? calls type_of(x)::from_residual() + QuestionFromResidual, + /// try { ..; x } calls type_of(x)::from_output(x) + TryBlockFromOutput, +} + +impl CallDesugaringKind { + pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId { + match self { + Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(), + Self::QuestionBranch | Self::TryBlockFromOutput => { + tcx.lang_items().try_trait().unwrap() + } + Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CallKind<'tcx> { + /// A normal method call of the form `receiver.foo(a, b, c)` + Normal { + self_arg: Option, + desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>, + /// Whether the self type of the method call has an `.as_ref()` method. + /// Used for better diagnostics. + is_option_or_result: bool, + }, + /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)` + FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> }, + /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) + Operator { self_arg: Option, trait_id: DefId, self_ty: Ty<'tcx> }, + DerefCoercion { + /// The `Span` of the `Target` associated type + /// in the `Deref` impl we are using. + deref_target: Span, + /// The type `T::Deref` we are dereferencing to + deref_target_ty: Ty<'tcx>, + self_ty: Ty<'tcx>, + }, +} + +pub fn call_kind<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + method_did: DefId, + method_substs: SubstsRef<'tcx>, + fn_call_span: Span, + from_hir_call: bool, + self_arg: Option, +) -> CallKind<'tcx> { + let parent = tcx.opt_associated_item(method_did).and_then(|assoc| match assoc.container { + AssocItemContainer::ImplContainer(impl_did) => tcx.trait_id_of_impl(impl_did), + AssocItemContainer::TraitContainer(trait_did) => Some(trait_did), + }); + + let fn_call = parent + .and_then(|p| tcx.lang_items().group(LangItemGroup::Fn).iter().find(|did| **did == p)); + + let operator = (!from_hir_call) + .then(|| parent) + .flatten() + .and_then(|p| tcx.lang_items().group(LangItemGroup::Op).iter().find(|did| **did == p)); + + let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); + + // Check for a 'special' use of 'self' - + // an FnOnce call, an operator (e.g. `<<`), or a + // deref coercion. + let kind = if let Some(&trait_id) = fn_call { + Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) }) + } else if let Some(&trait_id) = operator { + Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) }) + } else if is_deref { + let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { + Instance::resolve(tcx, param_env, deref_target, method_substs).transpose() + }); + if let Some(Ok(instance)) = deref_target { + let deref_target_ty = instance.ty(tcx, param_env); + Some(CallKind::DerefCoercion { + deref_target: tcx.def_span(instance.def_id()), + deref_target_ty, + self_ty: method_substs.type_at(0), + }) + } else { + None + } + } else { + None + }; + + kind.unwrap_or_else(|| { + // This isn't a 'special' use of `self` + debug!(?method_did, ?fn_call_span); + let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn() + && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) + { + Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0))) + } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) { + if Some(method_did) == tcx.lang_items().branch_fn() { + Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0))) + } else if Some(method_did) == tcx.lang_items().from_residual_fn() { + Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0))) + } else { + None + } + } else if Some(method_did) == tcx.lang_items().from_output_fn() + && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock) + { + Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0))) + } else { + None + }; + let parent_self_ty = tcx + .parent(method_did) + .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) + .and_then(|did| match tcx.type_of(did).kind() { + ty::Adt(def, ..) => Some(def.did), + _ => None, + }); + let is_option_or_result = parent_self_ty.map_or(false, |def_id| { + matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) + }); + CallKind::Normal { self_arg, desugaring, is_option_or_result } + }) +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 4a406f8bfd..a1876bed83 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -1,8 +1,10 @@ pub mod aggregate; mod alignment; +mod call_kind; pub mod collect_writes; mod find_self_call; pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; +pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind}; pub use self::find_self_call::find_self_call; diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index e3395df359..ad296c9765 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] arrayvec = { version = "0.7", default-features = false } ena = "0.14" -indexmap = "1.5.1" +indexmap = { version = "1.8.0", features = ["rustc-rayon"] } tracing = "0.1" jobserver_crate = { version = "0.1.13", package = "jobserver" } rustc_serialize = { path = "../rustc_serialize" } @@ -17,8 +17,8 @@ rustc_macros = { path = "../rustc_macros" } rustc_graphviz = { path = "../rustc_graphviz" } cfg-if = "0.1.2" stable_deref_trait = "1.0.0" -rayon = { version = "0.3.1", package = "rustc-rayon" } -rayon-core = { version = "0.3.1", package = "rustc-rayon-core" } +rayon = { version = "0.3.2", package = "rustc-rayon" } +rayon-core = { version = "0.3.2", package = "rustc-rayon-core" } rustc-hash = "1.1.0" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_index = { path = "../rustc_index", package = "rustc_index" } diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index c9af35da4b..e931379dd3 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -3,6 +3,9 @@ use rustc_serialize::{Decodable, Encodable}; use std::convert::TryInto; use std::hash::{Hash, Hasher}; +#[cfg(test)] +mod tests; + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] #[repr(C)] pub struct Fingerprint(u64, u64); @@ -54,7 +57,7 @@ impl Fingerprint { let c = a.wrapping_add(b); - Fingerprint((c >> 64) as u64, c as u64) + Fingerprint(c as u64, (c >> 64) as u64) } pub fn to_hex(&self) -> String { @@ -149,10 +152,10 @@ impl Encodable for Fingerprint { impl Decodable for Fingerprint { #[inline] - fn decode(d: &mut D) -> Result { + fn decode(d: &mut D) -> Self { let mut bytes = [0u8; 16]; - d.read_raw_bytes_into(&mut bytes)?; - Ok(Fingerprint::from_le_bytes(bytes)) + d.read_raw_bytes_into(&mut bytes); + Fingerprint::from_le_bytes(bytes) } } @@ -195,8 +198,8 @@ impl Encodable for PackedFingerprint { impl Decodable for PackedFingerprint { #[inline] - fn decode(d: &mut D) -> Result { - Fingerprint::decode(d).map(PackedFingerprint) + fn decode(d: &mut D) -> Self { + Self(Fingerprint::decode(d)) } } diff --git a/compiler/rustc_data_structures/src/fingerprint/tests.rs b/compiler/rustc_data_structures/src/fingerprint/tests.rs new file mode 100644 index 0000000000..9b0783e33a --- /dev/null +++ b/compiler/rustc_data_structures/src/fingerprint/tests.rs @@ -0,0 +1,14 @@ +use super::*; + +// Check that `combine_commutative` is order independent. +#[test] +fn combine_commutative_is_order_independent() { + let a = Fingerprint::new(0xf6622fb349898b06, 0x70be9377b2f9c610); + let b = Fingerprint::new(0xa9562bf5a2a5303c, 0x67d9b6c82034f13d); + let c = Fingerprint::new(0x0d013a27811dbbc3, 0x9a3f7b3d9142ec43); + let permutations = [(a, b, c), (a, c, b), (b, a, c), (b, c, a), (c, a, b), (c, b, a)]; + let f = a.combine_commutative(b).combine_commutative(c); + for p in &permutations { + assert_eq!(f, p.0.combine_commutative(p.1).combine_commutative(p.2)); + } +} diff --git a/compiler/rustc_data_structures/src/intern.rs b/compiler/rustc_data_structures/src/intern.rs new file mode 100644 index 0000000000..c79a5ebf09 --- /dev/null +++ b/compiler/rustc_data_structures/src/intern.rs @@ -0,0 +1,98 @@ +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; +use std::ptr; + +mod private { + #[derive(Clone, Copy, Debug)] + pub struct PrivateZst; +} + +/// A reference to a value that is interned, and is known to be unique. +/// +/// Note that it is possible to have a `T` and a `Interned` that are (or +/// refer to) equal but different values. But if you have two different +/// `Interned`s, they both refer to the same value, at a single location in +/// memory. This means that equality and hashing can be done on the value's +/// address rather than the value's contents, which can improve performance. +/// +/// The `PrivateZst` field means you can pattern match with `Interned(v, _)` +/// but you can only construct a `Interned` with `new_unchecked`, and not +/// directly. +#[derive(Debug)] +#[cfg_attr(not(bootstrap), rustc_pass_by_value)] +pub struct Interned<'a, T>(pub &'a T, pub private::PrivateZst); + +impl<'a, T> Interned<'a, T> { + /// Create a new `Interned` value. The value referred to *must* be interned + /// and thus be unique, and it *must* remain unique in the future. This + /// function has `_unchecked` in the name but is not `unsafe`, because if + /// the uniqueness condition is violated condition it will cause incorrect + /// behaviour but will not affect memory safety. + #[inline] + pub const fn new_unchecked(t: &'a T) -> Self { + Interned(t, private::PrivateZst) + } +} + +impl<'a, T> Clone for Interned<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> Copy for Interned<'a, T> {} + +impl<'a, T> Deref for Interned<'a, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + self.0 + } +} + +impl<'a, T> PartialEq for Interned<'a, T> { + #[inline] + fn eq(&self, other: &Self) -> bool { + // Pointer equality implies equality, due to the uniqueness constraint. + ptr::eq(self.0, other.0) + } +} + +impl<'a, T> Eq for Interned<'a, T> {} + +// In practice you can't intern any `T` that doesn't implement `Eq`, because +// that's needed for hashing. Therefore, we won't be interning any `T` that +// implements `PartialOrd` without also implementing `Ord`. So we can have the +// bound `T: Ord` here and avoid duplication with the `Ord` impl below. +impl<'a, T: Ord> PartialOrd for Interned<'a, T> { + fn partial_cmp(&self, other: &Interned<'a, T>) -> Option { + Some(self.cmp(other)) + } +} + +impl<'a, T: Ord> Ord for Interned<'a, T> { + fn cmp(&self, other: &Interned<'a, T>) -> Ordering { + // Pointer equality implies equality, due to the uniqueness constraint, + // but the contents must be compared otherwise. + if ptr::eq(self.0, other.0) { + Ordering::Equal + } else { + let res = self.0.cmp(&other.0); + debug_assert_ne!(res, Ordering::Equal); + res + } + } +} + +impl<'a, T> Hash for Interned<'a, T> { + #[inline] + fn hash(&self, s: &mut H) { + // Pointer hashing is sufficient, due to the uniqueness constraint. + ptr::hash(self.0, s) + } +} + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_data_structures/src/intern/tests.rs b/compiler/rustc_data_structures/src/intern/tests.rs new file mode 100644 index 0000000000..09810a0850 --- /dev/null +++ b/compiler/rustc_data_structures/src/intern/tests.rs @@ -0,0 +1,59 @@ +use super::*; +use std::cmp::Ordering; + +#[derive(Debug)] +struct S(u32); + +impl PartialEq for S { + fn eq(&self, _other: &Self) -> bool { + panic!("shouldn't be called"); + } +} + +impl Eq for S {} + +impl PartialOrd for S { + fn partial_cmp(&self, other: &S) -> Option { + // The `==` case should be handled by `Interned`. + assert_ne!(self.0, other.0); + self.0.partial_cmp(&other.0) + } +} + +impl Ord for S { + fn cmp(&self, other: &S) -> Ordering { + // The `==` case should be handled by `Interned`. + assert_ne!(self.0, other.0); + self.0.cmp(&other.0) + } +} + +#[test] +fn test_uniq() { + let s1 = S(1); + let s2 = S(2); + let s3 = S(3); + let s4 = S(1); // violates uniqueness + + let v1 = Interned::new_unchecked(&s1); + let v2 = Interned::new_unchecked(&s2); + let v3a = Interned::new_unchecked(&s3); + let v3b = Interned::new_unchecked(&s3); + let v4 = Interned::new_unchecked(&s4); // violates uniqueness + + assert_ne!(v1, v2); + assert_ne!(v2, v3a); + assert_eq!(v1, v1); + assert_eq!(v3a, v3b); + assert_ne!(v1, v4); // same content but different addresses: not equal + + assert_eq!(v1.cmp(&v2), Ordering::Less); + assert_eq!(v3a.cmp(&v2), Ordering::Greater); + assert_eq!(v1.cmp(&v1), Ordering::Equal); // only uses Interned::eq, not S::cmp + assert_eq!(v3a.cmp(&v3b), Ordering::Equal); // only uses Interned::eq, not S::cmp + + assert_eq!(v1.partial_cmp(&v2), Some(Ordering::Less)); + assert_eq!(v3a.partial_cmp(&v2), Some(Ordering::Greater)); + assert_eq!(v1.partial_cmp(&v1), Some(Ordering::Equal)); // only uses Interned::eq, not S::cmp + assert_eq!(v3a.partial_cmp(&v3b), Some(Ordering::Equal)); // only uses Interned::eq, not S::cmp +} diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 181e5180d5..80f83140f4 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -21,11 +21,13 @@ #![feature(type_alias_impl_trait)] #![feature(new_uninit)] #![feature(once_cell)] +#![feature(rustc_attrs)] #![feature(test)] #![feature(thread_id_value)] #![feature(vec_into_raw_parts)] #![allow(rustc::default_hash_types)] #![deny(unaligned_references)] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; @@ -67,12 +69,12 @@ pub mod flock; pub mod functor; pub mod fx; pub mod graph; +pub mod intern; pub mod jobserver; pub mod macros; pub mod map_in_place; pub mod obligation_forest; pub mod owning_ref; -pub mod ptr_key; pub mod sip128; pub mod small_c_str; pub mod snapshot_map; diff --git a/compiler/rustc_data_structures/src/ptr_key.rs b/compiler/rustc_data_structures/src/ptr_key.rs deleted file mode 100644 index 440ccb05d8..0000000000 --- a/compiler/rustc_data_structures/src/ptr_key.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::ops::Deref; -use std::{hash, ptr}; - -/// A wrapper around reference that compares and hashes like a pointer. -/// Can be used as a key in sets/maps indexed by pointers to avoid `unsafe`. -#[derive(Debug)] -pub struct PtrKey<'a, T>(pub &'a T); - -impl<'a, T> Clone for PtrKey<'a, T> { - fn clone(&self) -> Self { - *self - } -} - -impl<'a, T> Copy for PtrKey<'a, T> {} - -impl<'a, T> PartialEq for PtrKey<'a, T> { - fn eq(&self, rhs: &Self) -> bool { - ptr::eq(self.0, rhs.0) - } -} - -impl<'a, T> Eq for PtrKey<'a, T> {} - -impl<'a, T> hash::Hash for PtrKey<'a, T> { - fn hash(&self, hasher: &mut H) { - (self.0 as *const T).hash(hasher) - } -} - -impl<'a, T> Deref for PtrKey<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.0 - } -} diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index 53062b9c20..6e5c0617bf 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -202,28 +202,26 @@ impl SipHasher128 { hasher } - // A specialized write function for values with size <= 8. #[inline] - fn short_write(&mut self, x: T) { - let size = mem::size_of::(); + pub fn short_write(&mut self, bytes: [u8; LEN]) { let nbuf = self.nbuf; - debug_assert!(size <= 8); + debug_assert!(LEN <= 8); debug_assert!(nbuf < BUFFER_SIZE); - debug_assert!(nbuf + size < BUFFER_WITH_SPILL_SIZE); + debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE); - if nbuf + size < BUFFER_SIZE { + if nbuf + LEN < BUFFER_SIZE { unsafe { // The memcpy call is optimized away because the size is known. let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf); - ptr::copy_nonoverlapping(&x as *const _ as *const u8, dst, size); + ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN); } - self.nbuf = nbuf + size; + self.nbuf = nbuf + LEN; return; } - unsafe { self.short_write_process_buffer(x) } + unsafe { self.short_write_process_buffer(bytes) } } // A specialized write function for values with size <= 8 that should only @@ -233,18 +231,17 @@ impl SipHasher128 { // `self.nbuf` must cause `self.buf` to become fully initialized (and not // overflow) if it wasn't already. #[inline(never)] - unsafe fn short_write_process_buffer(&mut self, x: T) { - let size = mem::size_of::(); + unsafe fn short_write_process_buffer(&mut self, bytes: [u8; LEN]) { let nbuf = self.nbuf; - debug_assert!(size <= 8); + debug_assert!(LEN <= 8); debug_assert!(nbuf < BUFFER_SIZE); - debug_assert!(nbuf + size >= BUFFER_SIZE); - debug_assert!(nbuf + size < BUFFER_WITH_SPILL_SIZE); + debug_assert!(nbuf + LEN >= BUFFER_SIZE); + debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE); // Copy first part of input into end of buffer, possibly into spill // element. The memcpy call is optimized away because the size is known. let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf); - ptr::copy_nonoverlapping(&x as *const _ as *const u8, dst, size); + ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN); // Process buffer. for i in 0..BUFFER_CAPACITY { @@ -254,17 +251,17 @@ impl SipHasher128 { self.state.v0 ^= elem; } - // Copy remaining input into start of buffer by copying size - 1 - // elements from spill (at most size - 1 bytes could have overflowed + // Copy remaining input into start of buffer by copying LEN - 1 + // elements from spill (at most LEN - 1 bytes could have overflowed // into the spill). The memcpy call is optimized away because the size - // is known. And the whole copy is optimized away for size == 1. + // is known. And the whole copy is optimized away for LEN == 1. let src = self.buf.get_unchecked(BUFFER_SPILL_INDEX) as *const _ as *const u8; - ptr::copy_nonoverlapping(src, self.buf.as_mut_ptr() as *mut u8, size - 1); + ptr::copy_nonoverlapping(src, self.buf.as_mut_ptr() as *mut u8, LEN - 1); // This function should only be called when the write fills the buffer. - // Therefore, when size == 1, the new `self.nbuf` must be zero. The size - // is statically known, so the branch is optimized away. - self.nbuf = if size == 1 { 0 } else { nbuf + size - BUFFER_SIZE }; + // Therefore, when LEN == 1, the new `self.nbuf` must be zero. + // LEN is statically known, so the branch is optimized away. + self.nbuf = if LEN == 1 { 0 } else { nbuf + LEN - BUFFER_SIZE }; self.processed += BUFFER_SIZE; } @@ -412,52 +409,52 @@ impl SipHasher128 { impl Hasher for SipHasher128 { #[inline] fn write_u8(&mut self, i: u8) { - self.short_write(i); + self.short_write(i.to_ne_bytes()); } #[inline] fn write_u16(&mut self, i: u16) { - self.short_write(i); + self.short_write(i.to_ne_bytes()); } #[inline] fn write_u32(&mut self, i: u32) { - self.short_write(i); + self.short_write(i.to_ne_bytes()); } #[inline] fn write_u64(&mut self, i: u64) { - self.short_write(i); + self.short_write(i.to_ne_bytes()); } #[inline] fn write_usize(&mut self, i: usize) { - self.short_write(i); + self.short_write(i.to_ne_bytes()); } #[inline] fn write_i8(&mut self, i: i8) { - self.short_write(i as u8); + self.short_write((i as u8).to_ne_bytes()); } #[inline] fn write_i16(&mut self, i: i16) { - self.short_write(i as u16); + self.short_write((i as u16).to_ne_bytes()); } #[inline] fn write_i32(&mut self, i: i32) { - self.short_write(i as u32); + self.short_write((i as u32).to_ne_bytes()); } #[inline] fn write_i64(&mut self, i: i64) { - self.short_write(i as u64); + self.short_write((i as u64).to_ne_bytes()); } #[inline] fn write_isize(&mut self, i: isize) { - self.short_write(i as usize); + self.short_write((i as usize).to_ne_bytes()); } #[inline] diff --git a/compiler/rustc_data_structures/src/snapshot_map/mod.rs b/compiler/rustc_data_structures/src/snapshot_map/mod.rs index b4cc85293f..8a50179cd3 100644 --- a/compiler/rustc_data_structures/src/snapshot_map/mod.rs +++ b/compiler/rustc_data_structures/src/snapshot_map/mod.rs @@ -13,6 +13,7 @@ mod tests; pub type SnapshotMapStorage = SnapshotMap, ()>; pub type SnapshotMapRef<'a, K, V, L> = SnapshotMap, &'a mut L>; +#[derive(Clone)] pub struct SnapshotMap, L = VecLog>> { map: M, undo_log: L, @@ -30,6 +31,7 @@ where } } +#[derive(Clone)] pub enum UndoLog { Inserted(K), Overwrite(K, V), diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 3da3517895..31d6a42cf2 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -80,22 +80,22 @@ impl Hasher for StableHasher { #[inline] fn write_u16(&mut self, i: u16) { - self.state.write_u16(i.to_le()); + self.state.short_write(i.to_le_bytes()); } #[inline] fn write_u32(&mut self, i: u32) { - self.state.write_u32(i.to_le()); + self.state.short_write(i.to_le_bytes()); } #[inline] fn write_u64(&mut self, i: u64) { - self.state.write_u64(i.to_le()); + self.state.short_write(i.to_le_bytes()); } #[inline] fn write_u128(&mut self, i: u128) { - self.state.write_u128(i.to_le()); + self.state.write(&i.to_le_bytes()); } #[inline] @@ -103,7 +103,7 @@ impl Hasher for StableHasher { // Always treat usize as u64 so we get the same results on 32 and 64 bit // platforms. This is important for symbol hashes when cross compiling, // for example. - self.state.write_u64((i as u64).to_le()); + self.state.short_write((i as u64).to_le_bytes()); } #[inline] @@ -113,31 +113,59 @@ impl Hasher for StableHasher { #[inline] fn write_i16(&mut self, i: i16) { - self.state.write_i16(i.to_le()); + self.state.short_write((i as u16).to_le_bytes()); } #[inline] fn write_i32(&mut self, i: i32) { - self.state.write_i32(i.to_le()); + self.state.short_write((i as u32).to_le_bytes()); } #[inline] fn write_i64(&mut self, i: i64) { - self.state.write_i64(i.to_le()); + self.state.short_write((i as u64).to_le_bytes()); } #[inline] fn write_i128(&mut self, i: i128) { - self.state.write_i128(i.to_le()); + self.state.write(&(i as u128).to_le_bytes()); } #[inline] fn write_isize(&mut self, i: isize) { - // Always treat isize as i64 so we get the same results on 32 and 64 bit + // Always treat isize as a 64-bit number so we get the same results on 32 and 64 bit // platforms. This is important for symbol hashes when cross compiling, // for example. Sign extending here is preferable as it means that the // same negative number hashes the same on both 32 and 64 bit platforms. - self.state.write_i64((i as i64).to_le()); + let value = i as u64; + + // Cold path + #[cold] + #[inline(never)] + fn hash_value(state: &mut SipHasher128, value: u64) { + state.write_u8(0xFF); + state.short_write(value.to_le_bytes()); + } + + // `isize` values often seem to have a small (positive) numeric value in practice. + // To exploit this, if the value is small, we will hash a smaller amount of bytes. + // However, we cannot just skip the leading zero bytes, as that would produce the same hash + // e.g. if you hash two values that have the same bit pattern when they are swapped. + // See https://github.com/rust-lang/rust/pull/93014 for context. + // + // Therefore, we employ the following strategy: + // 1) When we encounter a value that fits within a single byte (the most common case), we + // hash just that byte. This is the most common case that is being optimized. However, we do + // not do this for the value 0xFF, as that is a reserved prefix (a bit like in UTF-8). + // 2) When we encounter a larger value, we hash a "marker" 0xFF and then the corresponding + // 8 bytes. Since this prefix cannot occur when we hash a single byte, when we hash two + // `isize`s that fit within a different amount of bytes, they should always produce a different + // byte stream for the hasher. + if value < 0xFF { + self.state.write_u8(value as u8); + } else { + hash_value(&mut self.state, value); + } } } @@ -583,3 +611,22 @@ fn stable_hash_reduce( } } } + +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +pub enum NodeIdHashingMode { + Ignore, + HashDefPath, +} + +/// Controls what data we do or not not hash. +/// Whenever a `HashStable` implementation caches its +/// result, it needs to include `HashingControls` as part +/// of the key, to ensure that is does not produce an incorrect +/// result (for example, using a `Fingerprint` produced while +/// hashing `Span`s when a `Fingeprint` without `Span`s is +/// being requested) +#[derive(Clone, Hash, Eq, PartialEq, Debug)] +pub struct HashingControls { + pub hash_spans: bool, + pub node_id_hashing_mode: NodeIdHashingMode, +} diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs index 391db67d29..b0d66c32a0 100644 --- a/compiler/rustc_data_structures/src/stable_hasher/tests.rs +++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs @@ -39,7 +39,7 @@ fn test_hash_integers() { test_isize.hash(&mut h); // This depends on the hashing algorithm. See note at top of file. - let expected = (2736651863462566372, 8121090595289675650); + let expected = (1784307454142909076, 11471672289340283879); assert_eq!(h.finalize(), expected); } @@ -67,7 +67,7 @@ fn test_hash_isize() { test_isize.hash(&mut h); // This depends on the hashing algorithm. See note at top of file. - let expected = (14721296605626097289, 11385941877786388409); + let expected = (2789913510339652884, 674280939192711005); assert_eq!(h.finalize(), expected); } @@ -98,3 +98,66 @@ fn test_hash_bit_matrix() { assert_ne!(a, b); assert_ne!(hash(&a), hash(&b)); } + +// Check that exchanging the value of two adjacent fields changes the hash. +#[test] +fn test_attribute_permutation() { + macro_rules! test_type { + ($ty: ty) => {{ + struct Foo { + a: $ty, + b: $ty, + } + + impl HashStable for Foo { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.a.hash_stable(hcx, hasher); + self.b.hash_stable(hcx, hasher); + } + } + + #[allow(overflowing_literals)] + let mut item = Foo { a: 0xFF, b: 0xFF_FF }; + let hash_a = hash(&item); + std::mem::swap(&mut item.a, &mut item.b); + let hash_b = hash(&item); + assert_ne!( + hash_a, + hash_b, + "The hash stayed the same after values were swapped for type `{}`!", + stringify!($ty) + ); + }}; + } + + test_type!(u16); + test_type!(u32); + test_type!(u64); + test_type!(u128); + + test_type!(i16); + test_type!(i32); + test_type!(i64); + test_type!(i128); +} + +// Check that the `isize` hashing optimization does not produce the same hash when permuting two +// values. +#[test] +fn test_isize_compression() { + fn check_hash(a: u64, b: u64) { + let hash_a = hash(&(a as isize, b as isize)); + let hash_b = hash(&(b as isize, a as isize)); + assert_ne!( + hash_a, hash_b, + "The hash stayed the same when permuting values `{a}` and `{b}!", + ); + } + + check_hash(0xAA, 0xAAAA); + check_hash(0xFF, 0xFFFF); + check_hash(0xAAAA, 0xAAAAAA); + check_hash(0xAAAAAA, 0xAAAAAAAA); + check_hash(0xFF, 0xFFFFFFFFFFFFFFFF); + check_hash(u64::MAX /* -1 */, 1); +} diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index ce90fbacaa..12ef286091 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -55,8 +55,8 @@ impl Encodable for Svh { } impl Decodable for Svh { - fn decode(d: &mut D) -> Result { - d.read_u64().map(u64::from_le).map(Svh::new) + fn decode(d: &mut D) -> Svh { + Svh::new(u64::from_le(d.read_u64())) } } diff --git a/compiler/rustc_data_structures/src/thin_vec/tests.rs b/compiler/rustc_data_structures/src/thin_vec/tests.rs index 5abfd93937..0221b9912b 100644 --- a/compiler/rustc_data_structures/src/thin_vec/tests.rs +++ b/compiler/rustc_data_structures/src/thin_vec/tests.rs @@ -10,8 +10,8 @@ impl ThinVec { fn test_from_iterator() { assert_eq!(std::iter::empty().collect::>().into_vec(), Vec::::new()); assert_eq!(std::iter::once(42).collect::>().into_vec(), vec![42]); - assert_eq!(vec![1, 2].into_iter().collect::>().into_vec(), vec![1, 2]); - assert_eq!(vec![1, 2, 3].into_iter().collect::>().into_vec(), vec![1, 2, 3]); + assert_eq!([1, 2].into_iter().collect::>().into_vec(), vec![1, 2]); + assert_eq!([1, 2, 3].into_iter().collect::>().into_vec(), vec![1, 2, 3]); } #[test] diff --git a/compiler/rustc_data_structures/src/vec_map/tests.rs b/compiler/rustc_data_structures/src/vec_map/tests.rs index 9083de8598..458b60077d 100644 --- a/compiler/rustc_data_structures/src/vec_map/tests.rs +++ b/compiler/rustc_data_structures/src/vec_map/tests.rs @@ -14,7 +14,7 @@ fn test_from_iterator() { ); assert_eq!(std::iter::once((42, true)).collect::>().into_vec(), vec![(42, true)]); assert_eq!( - vec![(1, true), (2, false)].into_iter().collect::>().into_vec(), + [(1, true), (2, false)].into_iter().collect::>().into_vec(), vec![(1, true), (2, false)] ); } @@ -41,7 +41,7 @@ fn test_insert() { #[test] fn test_get() { - let v = vec![(1, true), (2, false)].into_iter().collect::>(); + let v = [(1, true), (2, false)].into_iter().collect::>(); assert_eq!(v.get(&1), Some(&true)); assert_eq!(v.get(&2), Some(&false)); assert_eq!(v.get(&3), None); diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 694c679c15..85826cfbf0 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -8,6 +8,7 @@ #![feature(nll)] #![feature(once_cell)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; @@ -28,7 +29,7 @@ use rustc_log::stdout_isatty; use rustc_metadata::locator; use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; -use rustc_serialize::json::{self, ToJson}; +use rustc_serialize::json::ToJson; use rustc_session::config::{nightly_options, CG_OPTIONS, DB_OPTIONS}; use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths}; use rustc_session::cstore::MetadataLoader; @@ -65,7 +66,7 @@ pub const EXIT_FAILURE: i32 = 1; const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\ ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md"; -const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["Z", "C", "crate-type"]; +const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"]; const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"]; @@ -215,17 +216,18 @@ fn run_compiler( } let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg")); + let check_cfg = interface::parse_check_cfg(matches.opt_strs("check-cfg")); let (odir, ofile) = make_output(&matches); let mut config = interface::Config { opts: sopts, crate_cfg: cfg, + crate_check_cfg: check_cfg, input: Input::File(PathBuf::new()), input_path: None, output_file: ofile, output_dir: odir, file_loader, diagnostic_output, - stderr: None, lint_caps: Default::default(), parse_sess_created: None, register_lints: None, @@ -262,7 +264,7 @@ fn run_compiler( describe_lints(compiler.session(), &lint_store, registered_lints); return; } - let should_stop = RustcDefaultCalls::print_crate_info( + let should_stop = print_crate_info( &***compiler.codegen_backend(), compiler.session(), None, @@ -291,7 +293,7 @@ fn run_compiler( interface::run_compiler(config, |compiler| { let sess = compiler.session(); - let should_stop = RustcDefaultCalls::print_crate_info( + let should_stop = print_crate_info( &***compiler.codegen_backend(), sess, Some(compiler.input()), @@ -300,13 +302,9 @@ fn run_compiler( compiler.temps_dir(), ) .and_then(|| { - RustcDefaultCalls::list_metadata( - sess, - &*compiler.codegen_backend().metadata_loader(), - compiler.input(), - ) + list_metadata(sess, &*compiler.codegen_backend().metadata_loader(), compiler.input()) }) - .and_then(|| RustcDefaultCalls::try_process_rlink(sess, compiler)); + .and_then(|| try_process_rlink(sess, compiler)); if should_stop == Compilation::Stop { return sess.compile_status(); @@ -511,10 +509,6 @@ impl Compilation { } } -/// CompilerCalls instance for a regular rustc build. -#[derive(Copy, Clone)] -pub struct RustcDefaultCalls; - fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { let upper_cased_code = code.to_ascii_uppercase(); let normalised = if upper_cased_code.starts_with('E') { @@ -587,162 +581,157 @@ fn show_content_with_pager(content: &str) { } } -impl RustcDefaultCalls { - pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation { - if sess.opts.debugging_opts.link_only { - if let Input::File(file) = compiler.input() { - // FIXME: #![crate_type] and #![crate_name] support not implemented yet - sess.init_crate_types(collect_crate_types(sess, &[])); - let outputs = compiler.build_output_filenames(sess, &[]); - let rlink_data = fs::read_to_string(file).unwrap_or_else(|err| { - sess.fatal(&format!("failed to read rlink file: {}", err)); - }); - let codegen_results: CodegenResults = - json::decode(&rlink_data).unwrap_or_else(|err| { - sess.fatal(&format!("failed to decode rlink: {}", err)); - }); - let result = compiler.codegen_backend().link(sess, codegen_results, &outputs); - abort_on_err(result, sess); - } else { - sess.fatal("rlink must be a file") - } - Compilation::Stop +pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation { + if sess.opts.debugging_opts.link_only { + if let Input::File(file) = compiler.input() { + // FIXME: #![crate_type] and #![crate_name] support not implemented yet + sess.init_crate_types(collect_crate_types(sess, &[])); + let outputs = compiler.build_output_filenames(sess, &[]); + let rlink_data = fs::read(file).unwrap_or_else(|err| { + sess.fatal(&format!("failed to read rlink file: {}", err)); + }); + let mut decoder = rustc_serialize::opaque::Decoder::new(&rlink_data, 0); + let codegen_results: CodegenResults = rustc_serialize::Decodable::decode(&mut decoder); + let result = compiler.codegen_backend().link(sess, codegen_results, &outputs); + abort_on_err(result, sess); } else { - Compilation::Continue + sess.fatal("rlink must be a file") } + Compilation::Stop + } else { + Compilation::Continue } +} - pub fn list_metadata( - sess: &Session, - metadata_loader: &dyn MetadataLoader, - input: &Input, - ) -> Compilation { - if sess.opts.debugging_opts.ls { - match *input { - Input::File(ref ifile) => { - let path = &(*ifile); - let mut v = Vec::new(); - locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v) - .unwrap(); - println!("{}", String::from_utf8(v).unwrap()); - } - Input::Str { .. } => { - early_error(ErrorOutputType::default(), "cannot list metadata for stdin"); - } +pub fn list_metadata( + sess: &Session, + metadata_loader: &dyn MetadataLoader, + input: &Input, +) -> Compilation { + if sess.opts.debugging_opts.ls { + match *input { + Input::File(ref ifile) => { + let path = &(*ifile); + let mut v = Vec::new(); + locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap(); + println!("{}", String::from_utf8(v).unwrap()); + } + Input::Str { .. } => { + early_error(ErrorOutputType::default(), "cannot list metadata for stdin"); } - return Compilation::Stop; } - - Compilation::Continue + return Compilation::Stop; } - fn print_crate_info( - codegen_backend: &dyn CodegenBackend, - sess: &Session, - input: Option<&Input>, - odir: &Option, - ofile: &Option, - temps_dir: &Option, - ) -> Compilation { - use rustc_session::config::PrintRequest::*; - // PrintRequest::NativeStaticLibs is special - printed during linking - // (empty iterator returns true) - if sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) { - return Compilation::Continue; - } + Compilation::Continue +} - let attrs = match input { - None => None, - Some(input) => { - let result = parse_crate_attrs(sess, input); - match result { - Ok(attrs) => Some(attrs), - Err(mut parse_error) => { - parse_error.emit(); - return Compilation::Stop; - } +fn print_crate_info( + codegen_backend: &dyn CodegenBackend, + sess: &Session, + input: Option<&Input>, + odir: &Option, + ofile: &Option, + temps_dir: &Option, +) -> Compilation { + use rustc_session::config::PrintRequest::*; + // NativeStaticLibs and LinkArgs are special - printed during linking + // (empty iterator returns true) + if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) { + return Compilation::Continue; + } + + let attrs = match input { + None => None, + Some(input) => { + let result = parse_crate_attrs(sess, input); + match result { + Ok(attrs) => Some(attrs), + Err(mut parse_error) => { + parse_error.emit(); + return Compilation::Stop; } } - }; - for req in &sess.opts.prints { - match *req { - TargetList => { - let mut targets = - rustc_target::spec::TARGETS.iter().copied().collect::>(); - targets.sort_unstable(); - println!("{}", targets.join("\n")); - } - Sysroot => println!("{}", sess.sysroot.display()), - TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()), - TargetSpec => println!("{}", sess.target.to_json().pretty()), - FileNames | CrateName => { - let input = input.unwrap_or_else(|| { - early_error(ErrorOutputType::default(), "no input file provided") - }); - let attrs = attrs.as_ref().unwrap(); - let t_outputs = rustc_interface::util::build_output_filenames( - input, odir, ofile, temps_dir, attrs, sess, - ); - let id = rustc_session::output::find_crate_name(sess, attrs, input); - if *req == PrintRequest::CrateName { - println!("{}", id); - continue; - } - let crate_types = collect_crate_types(sess, attrs); - for &style in &crate_types { - let fname = - rustc_session::output::filename_for_input(sess, style, &id, &t_outputs); - println!("{}", fname.file_name().unwrap().to_string_lossy()); - } + } + }; + for req in &sess.opts.prints { + match *req { + TargetList => { + let mut targets = rustc_target::spec::TARGETS.iter().copied().collect::>(); + targets.sort_unstable(); + println!("{}", targets.join("\n")); + } + Sysroot => println!("{}", sess.sysroot.display()), + TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()), + TargetSpec => println!("{}", sess.target.to_json().pretty()), + FileNames | CrateName => { + let input = input.unwrap_or_else(|| { + early_error(ErrorOutputType::default(), "no input file provided") + }); + let attrs = attrs.as_ref().unwrap(); + let t_outputs = rustc_interface::util::build_output_filenames( + input, odir, ofile, temps_dir, attrs, sess, + ); + let id = rustc_session::output::find_crate_name(sess, attrs, input); + if *req == PrintRequest::CrateName { + println!("{}", id); + continue; } - Cfg => { - let mut cfgs = sess - .parse_sess - .config - .iter() - .filter_map(|&(name, value)| { - // Note that crt-static is a specially recognized cfg - // directive that's printed out here as part of - // rust-lang/rust#37406, but in general the - // `target_feature` cfg is gated under - // rust-lang/rust#29717. For now this is just - // specifically allowing the crt-static cfg and that's - // it, this is intended to get into Cargo and then go - // through to build scripts. - if (name != sym::target_feature || value != Some(sym::crt_dash_static)) - && !sess.is_nightly_build() - && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some() - { - return None; - } - - if let Some(value) = value { - Some(format!("{}=\"{}\"", name, value)) - } else { - Some(name.to_string()) - } - }) - .collect::>(); - - cfgs.sort(); - for cfg in cfgs { - println!("{}", cfg); - } + let crate_types = collect_crate_types(sess, attrs); + for &style in &crate_types { + let fname = + rustc_session::output::filename_for_input(sess, style, &id, &t_outputs); + println!("{}", fname.file_name().unwrap().to_string_lossy()); } - RelocationModels - | CodeModels - | TlsModels - | TargetCPUs - | StackProtectorStrategies - | TargetFeatures => { - codegen_backend.print(*req, sess); + } + Cfg => { + let mut cfgs = sess + .parse_sess + .config + .iter() + .filter_map(|&(name, value)| { + // Note that crt-static is a specially recognized cfg + // directive that's printed out here as part of + // rust-lang/rust#37406, but in general the + // `target_feature` cfg is gated under + // rust-lang/rust#29717. For now this is just + // specifically allowing the crt-static cfg and that's + // it, this is intended to get into Cargo and then go + // through to build scripts. + if (name != sym::target_feature || value != Some(sym::crt_dash_static)) + && !sess.is_nightly_build() + && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some() + { + return None; + } + + if let Some(value) = value { + Some(format!("{}=\"{}\"", name, value)) + } else { + Some(name.to_string()) + } + }) + .collect::>(); + + cfgs.sort(); + for cfg in cfgs { + println!("{}", cfg); } - // Any output here interferes with Cargo's parsing of other printed output - PrintRequest::NativeStaticLibs => {} } + RelocationModels + | CodeModels + | TlsModels + | TargetCPUs + | StackProtectorStrategies + | TargetFeatures => { + codegen_backend.print(*req, sess); + } + // Any output here interferes with Cargo's parsing of other printed output + NativeStaticLibs => {} + LinkArgs => {} } - Compilation::Stop } + Compilation::Stop } /// Prints version information @@ -847,7 +836,7 @@ Available lint options: let builtin = sort_lints(sess, builtin); let (plugin_groups, builtin_groups): (Vec<_>, _) = - lint_store.get_lint_groups().iter().cloned().partition(|&(.., p)| p); + lint_store.get_lint_groups().partition(|&(.., p)| p); let plugin_groups = sort_lint_groups(plugin_groups); let builtin_groups = sort_lint_groups(builtin_groups); @@ -1101,31 +1090,31 @@ fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, Vec Option<(Vec, bool)> { - let args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).collect::>(); - - // Avoid printing help because of empty args. This can suggest the compiler - // itself is not the program root (consider RLS). - if args.len() < 2 { - return None; - } + let mut args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable(); - let matches = handle_options(&args)?; let mut result = Vec::new(); let mut excluded_cargo_defaults = false; - for flag in ICE_REPORT_COMPILER_FLAGS { - let prefix = if flag.len() == 1 { "-" } else { "--" }; - - for content in &matches.opt_strs(flag) { - // Split always returns the first element - let name = if let Some(first) = content.split('=').next() { first } else { &content }; - - let content = - if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&name) { name } else { content }; - - if !ICE_REPORT_COMPILER_FLAGS_EXCLUDE.contains(&name) { - result.push(format!("{}{} {}", prefix, flag, content)); + while let Some(arg) = args.next() { + if let Some(a) = ICE_REPORT_COMPILER_FLAGS.iter().find(|a| arg.starts_with(*a)) { + let content = if arg.len() == a.len() { + match args.next() { + Some(arg) => arg.to_string(), + None => continue, + } + } else if arg.get(a.len()..a.len() + 1) == Some("=") { + arg[a.len() + 1..].to_string() } else { + arg[a.len()..].to_string() + }; + if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| content.starts_with(exc)) { excluded_cargo_defaults = true; + } else { + result.push(a.to_string()); + match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| content.starts_with(*s)) + { + Some(s) => result.push(s.to_string()), + None => result.push(content), + } } } } @@ -1241,6 +1230,15 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { /// /// A custom rustc driver can skip calling this to set up a custom ICE hook. pub fn install_ice_hook() { + // If the user has not explicitly overriden "RUST_BACKTRACE", then produce + // full backtraces. When a compiler ICE happens, we want to gather + // as much information as possible to present in the issue opened + // by the user. Compiler developers and other rustc users can + // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE" + // (e.g. `RUST_BACKTRACE=1`) + if std::env::var("RUST_BACKTRACE").is_err() { + std::env::set_var("RUST_BACKTRACE", "full"); + } SyncLazy::force(&DEFAULT_HOOK); } diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 79d9c55b54..a72681dbf4 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -97,6 +97,7 @@ E0184: include_str!("./error_codes/E0184.md"), E0185: include_str!("./error_codes/E0185.md"), E0186: include_str!("./error_codes/E0186.md"), E0191: include_str!("./error_codes/E0191.md"), +E0192: include_str!("./error_codes/E0192.md"), E0193: include_str!("./error_codes/E0193.md"), E0195: include_str!("./error_codes/E0195.md"), E0197: include_str!("./error_codes/E0197.md"), @@ -472,6 +473,7 @@ E0768: include_str!("./error_codes/E0768.md"), E0769: include_str!("./error_codes/E0769.md"), E0770: include_str!("./error_codes/E0770.md"), E0771: include_str!("./error_codes/E0771.md"), +E0772: include_str!("./error_codes/E0772.md"), E0773: include_str!("./error_codes/E0773.md"), E0774: include_str!("./error_codes/E0774.md"), E0775: include_str!("./error_codes/E0775.md"), @@ -486,6 +488,7 @@ E0783: include_str!("./error_codes/E0783.md"), E0784: include_str!("./error_codes/E0784.md"), E0785: include_str!("./error_codes/E0785.md"), E0786: include_str!("./error_codes/E0786.md"), +E0787: include_str!("./error_codes/E0787.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -520,7 +523,6 @@ E0786: include_str!("./error_codes/E0786.md"), // E0188, // can not cast an immutable reference to a mutable pointer // E0189, // deprecated: can only cast a boxed pointer to a boxed object // E0190, // deprecated: can only cast a &-pointer to an &-object -// E0192, // negative impl only applicable to auto traits // E0194, // merged into E0403 // E0196, // cannot determine a type for this closure E0208, @@ -641,5 +643,4 @@ E0786: include_str!("./error_codes/E0786.md"), // E0723, // unstable feature in `const` context E0726, // non-explicit (not `'_`) elided lifetime in unsupported position // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. - E0772, // `'static' obligation coming from `impl dyn Trait {}` or `impl Foo for dyn Bar {}`. } diff --git a/compiler/rustc_error_codes/src/error_codes/E0038.md b/compiler/rustc_error_codes/src/error_codes/E0038.md index ca2eaa5405..584b78554e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0038.md +++ b/compiler/rustc_error_codes/src/error_codes/E0038.md @@ -15,7 +15,7 @@ Two general aspects of trait object types give rise to the restrictions: these types can only be accessed through pointers, such as `&dyn Trait` or `Box`. The size of such a pointer is known, but the size of the `dyn Trait` object pointed-to by the pointer is _opaque_ to code working - with it, and different tait objects with the same trait object type may + with it, and different trait objects with the same trait object type may have different sizes. 2. The pointer used to access a trait object is paired with an extra pointer @@ -167,7 +167,7 @@ implementation on-demand. If you call `foo()` with a `bool` parameter, the compiler will only generate code for `foo::()`. When we have additional type parameters, the number of monomorphized implementations the compiler generates does not grow drastically, since the compiler will only generate an -implementation if the function is called with unparametrized substitutions +implementation if the function is called with unparameterized substitutions (i.e., substitutions where none of the substituted types are themselves parameterized). diff --git a/compiler/rustc_error_codes/src/error_codes/E0183.md b/compiler/rustc_error_codes/src/error_codes/E0183.md index 7e1d08daae..92fa4c7c21 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0183.md +++ b/compiler/rustc_error_codes/src/error_codes/E0183.md @@ -1,4 +1,4 @@ -Manual implemetation of a `Fn*` trait. +Manual implementation of a `Fn*` trait. Erroneous code example: @@ -33,7 +33,7 @@ impl FnOnce<()> for MyClosure { // ok! } ``` -The argumements must be a tuple representing the argument list. +The arguments must be a tuple representing the argument list. For more info, see the [tracking issue][iss29625]: [iss29625]: https://github.com/rust-lang/rust/issues/29625 diff --git a/compiler/rustc_error_codes/src/error_codes/E0192.md b/compiler/rustc_error_codes/src/error_codes/E0192.md index 5fd951b2e8..deca042a91 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0192.md +++ b/compiler/rustc_error_codes/src/error_codes/E0192.md @@ -1,15 +1,17 @@ +#### Note: this error code is no longer emitted by the compiler. + A negative impl was added on a trait implementation. Erroneous code example: -```compile_fail,E0192 +```compile_fail trait Trait { type Bar; } struct Foo; -impl !Trait for Foo { } //~ ERROR E0192 +impl !Trait for Foo { } //~ ERROR fn main() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0521.md b/compiler/rustc_error_codes/src/error_codes/E0521.md index 65dcac983a..fedf6365fb 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0521.md +++ b/compiler/rustc_error_codes/src/error_codes/E0521.md @@ -10,7 +10,7 @@ let _add = |el: &str| { }; ``` -A type anotation of a closure parameter implies a new lifetime declaration. +A type annotation of a closure parameter implies a new lifetime declaration. Consider to drop it, the compiler is reliably able to infer them. ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0581.md b/compiler/rustc_error_codes/src/error_codes/E0581.md index 89f6e3269e..02468dd946 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0581.md +++ b/compiler/rustc_error_codes/src/error_codes/E0581.md @@ -10,7 +10,7 @@ fn main() { } ``` -The problem here is that the lifetime isn't contrained by any of the arguments, +The problem here is that the lifetime isn't constrained by any of the arguments, making it impossible to determine how long it's supposed to live. To fix this issue, either use the lifetime in the arguments, or use the diff --git a/compiler/rustc_error_codes/src/error_codes/E0604.md b/compiler/rustc_error_codes/src/error_codes/E0604.md index adbf76509e..806f0001c6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0604.md +++ b/compiler/rustc_error_codes/src/error_codes/E0604.md @@ -6,11 +6,16 @@ Erroneous code example: 0u32 as char; // error: only `u8` can be cast as `char`, not `u32` ``` -As the error message indicates, only `u8` can be cast into `char`. Example: +`char` is a Unicode Scalar Value, an integer value from 0 to 0xD7FF and +0xE000 to 0x10FFFF. (The gap is for surrogate pairs.) Only `u8` always fits in +those ranges so only `u8` may be cast to `char`. + +To allow larger values, use `char::from_u32`, which checks the value is valid. ``` -let c = 86u8 as char; // ok! -assert_eq!(c, 'V'); +assert_eq!(86u8 as char, 'V'); // ok! +assert_eq!(char::from_u32(0x3B1), Some('α')); // ok! +assert_eq!(char::from_u32(0xD800), None); // not a USV. ``` For more information about casts, take a look at the Type cast section in diff --git a/compiler/rustc_error_codes/src/error_codes/E0660.md b/compiler/rustc_error_codes/src/error_codes/E0660.md index 26d35f2620..abf9027591 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0660.md +++ b/compiler/rustc_error_codes/src/error_codes/E0660.md @@ -1,12 +1,9 @@ +#### Note: this error code is no longer emitted by the compiler. + The argument to the `llvm_asm` macro is not well-formed. Erroneous code example: -```compile_fail,E0660 +```ignore (no longer emitted) llvm_asm!("nop" "nop"); ``` - -Considering that this would be a long explanation, we instead recommend you -take a look at the [`llvm_asm`] chapter of the Unstable book: - -[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0661.md b/compiler/rustc_error_codes/src/error_codes/E0661.md index 0b8ba7fbbe..245f755cdd 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0661.md +++ b/compiler/rustc_error_codes/src/error_codes/E0661.md @@ -1,13 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + An invalid syntax was passed to the second argument of an `llvm_asm` macro line. Erroneous code example: -```compile_fail,E0661 +```ignore (no longer emitted) let a; llvm_asm!("nop" : "r"(a)); ``` - -Considering that this would be a long explanation, we instead recommend you -take a look at the [`llvm_asm`] chapter of the Unstable book: - -[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0662.md b/compiler/rustc_error_codes/src/error_codes/E0662.md index 8c1bab8d04..ffb716f995 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0662.md +++ b/compiler/rustc_error_codes/src/error_codes/E0662.md @@ -1,16 +1,13 @@ +#### Note: this error code is no longer emitted by the compiler. + An invalid input operand constraint was passed to the `llvm_asm` macro (third line). Erroneous code example: -```compile_fail,E0662 +```ignore (no longer emitted) llvm_asm!("xor %eax, %eax" : : "=test"("a") ); ``` - -Considering that this would be a long explanation, we instead recommend you -take a look at the [`llvm_asm`] chapter of the Unstable book: - -[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0663.md b/compiler/rustc_error_codes/src/error_codes/E0663.md index 53ffd3373a..351cfaca29 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0663.md +++ b/compiler/rustc_error_codes/src/error_codes/E0663.md @@ -1,16 +1,13 @@ +#### Note: this error code is no longer emitted by the compiler. + An invalid input operand constraint was passed to the `llvm_asm` macro (third line). Erroneous code example: -```compile_fail,E0663 +```ignore (no longer emitted) llvm_asm!("xor %eax, %eax" : : "+test"("a") ); ``` - -Considering that this would be a long explanation, we instead recommend you -take a look at the [`llvm_asm`] chapter of the Unstable book: - -[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0664.md b/compiler/rustc_error_codes/src/error_codes/E0664.md index f8e72cd330..34135d5db3 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0664.md +++ b/compiler/rustc_error_codes/src/error_codes/E0664.md @@ -1,16 +1,13 @@ +#### Note: this error code is no longer emitted by the compiler. + A clobber was surrounded by braces in the `llvm_asm` macro. Erroneous code example: -```compile_fail,E0664 +```ignore (no longer emitted) llvm_asm!("mov $$0x200, %eax" : : : "{eax}" ); ``` - -Considering that this would be a long explanation, we instead recommend you -take a look at the [`llvm_asm`] chapter of the Unstable book: - -[`llvm_asm`]: https://doc.rust-lang.org/stable/unstable-book/library-features/llvm-asm.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0668.md b/compiler/rustc_error_codes/src/error_codes/E0668.md index b6fedfe53f..393aabe289 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0668.md +++ b/compiler/rustc_error_codes/src/error_codes/E0668.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + Malformed inline assembly rejected by LLVM. Erroneous code example: -```compile_fail,E0668 +```ignore (no longer emitted) #![feature(llvm_asm)] fn main() { diff --git a/compiler/rustc_error_codes/src/error_codes/E0669.md b/compiler/rustc_error_codes/src/error_codes/E0669.md index f078c441b3..2be8f04eda 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0669.md +++ b/compiler/rustc_error_codes/src/error_codes/E0669.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + Cannot convert inline assembly operand to a single LLVM value. Erroneous code example: -```compile_fail,E0669 +```ignore (no longer emitted) #![feature(llvm_asm)] fn main() { diff --git a/compiler/rustc_error_codes/src/error_codes/E0772.md b/compiler/rustc_error_codes/src/error_codes/E0772.md new file mode 100644 index 0000000000..262e52351e --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0772.md @@ -0,0 +1,89 @@ +A trait object has some specific lifetime `'1`, but it was used in a way that +requires it to have a `'static` lifetime. + +Example of erroneous code: + +```compile_fail,E0772 +trait BooleanLike {} +trait Person {} + +impl BooleanLike for bool {} + +impl dyn Person { + fn is_cool(&self) -> bool { + // hey you, you're pretty cool + true + } +} + +fn get_is_cool<'p>(person: &'p dyn Person) -> impl BooleanLike { + // error: `person` has an anonymous lifetime `'p` but calling + // `print_cool_fn` introduces an implicit `'static` lifetime + // requirement + person.is_cool() +} +``` + +The trait object `person` in the function `get_is_cool`, while already being +behind a reference with lifetime `'p`, also has it's own implicit lifetime, +`'2`. + +Lifetime `'2` represents the data the trait object might hold inside, for +example: + +``` +trait MyTrait {} + +struct MyStruct<'a>(&'a i32); + +impl<'a> MyTrait for MyStruct<'a> {} +``` + +With this scenario, if a trait object of `dyn MyTrait + '2` was made from +`MyStruct<'a>`, `'a` must live as long, if not longer than `'2`. This allows the +trait object's internal data to be accessed safely from any trait methods. This +rule also goes for any lifetime any struct made into a trait object may have. + +In the implementation for `dyn Person`, the `'2` lifetime representing the +internal data was ommitted, meaning that the compiler inferred the lifetime +`'static`. As a result, the implementation's `is_cool` is inferred by the +compiler to look like this: + +``` +# trait Person {} +# +# impl dyn Person { +fn is_cool<'a>(self: &'a (dyn Person + 'static)) -> bool {unimplemented!()} +# } +``` + +While the `get_is_cool` function is inferred to look like this: + +``` +# trait Person {} +# trait BooleanLike {} +# +fn get_is_cool<'p, R: BooleanLike>(person: &'p (dyn Person + 'p)) -> R { + unimplemented!() +} +``` + +Which brings us to the core of the problem; the assignment of type +`&'_ (dyn Person + '_)` to type `&'_ (dyn Person + 'static)` is impossible. + +Fixing it is as simple as being generic over lifetime `'2`, as to prevent the +compiler from inferring it as `'static`: + +``` +# trait Person {} +# +impl<'d> dyn Person + 'd {/* ... */} + +// This works too, and is more elegant: +//impl dyn Person + '_ {/* ... */} +``` + +See the [Rust Reference on Trait Object Lifetime Bounds][trait-objects] for +more information on trait object lifetimes. + +[trait-object-lifetime-bounds]: https://doc.rust-lang.org/reference/types/trait-object.html#trait-object-lifetime-bounds diff --git a/compiler/rustc_error_codes/src/error_codes/E0787.md b/compiler/rustc_error_codes/src/error_codes/E0787.md new file mode 100644 index 0000000000..cee5082927 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0787.md @@ -0,0 +1,28 @@ +An unsupported naked function definition. + +Erroneous code example: + +```compile_fail,E0787 +#![feature(naked_functions)] + +#[naked] +pub extern "C" fn f() -> u32 { + 42 +} +``` + +The naked functions must be defined using a single inline assembly +block. + +The execution must never fall through past the end of the assembly +code so the block must use `noreturn` option. The asm block can also +use `att_syntax` and `raw` options, but others options are not allowed. + +The asm block must not contain any operands other than `const` and +`sym`. + +### Additional information + +For more information, please see [RFC 2972]. + +[RFC 2972]: https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index e5116cd8df..8cfecafd20 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -11,6 +11,11 @@ use rustc_span::{MultiSpan, Span, DUMMY_SP}; use std::fmt; use std::hash::{Hash, Hasher}; +/// Error type for `Diagnostic`'s `suggestions` field, indicating that +/// `.disable_suggestions()` was called on the `Diagnostic`. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub struct SuggestionsDisabled; + #[must_use] #[derive(Clone, Debug, Encodable, Decodable)] pub struct Diagnostic { @@ -19,7 +24,7 @@ pub struct Diagnostic { pub code: Option, pub span: MultiSpan, pub children: Vec, - pub suggestions: Vec, + pub suggestions: Result, SuggestionsDisabled>, /// This is not used for highlighting or rendering any error message. Rather, it can be used /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of @@ -106,7 +111,7 @@ impl Diagnostic { code, span: MultiSpan::new(), children: vec![], - suggestions: vec![], + suggestions: Ok(vec![]), sort_span: DUMMY_SP, is_lint: false, } @@ -300,6 +305,21 @@ impl Diagnostic { self } + /// Disallow attaching suggestions this diagnostic. + /// Any suggestions attached e.g. with the `span_suggestion_*` methods + /// (before and after the call to `disable_suggestions`) will be ignored. + pub fn disable_suggestions(&mut self) -> &mut Self { + self.suggestions = Err(SuggestionsDisabled); + self + } + + /// Helper for pushing to `self.suggestions`, if available (not disable). + fn push_suggestion(&mut self, suggestion: CodeSuggestion) { + if let Ok(suggestions) = &mut self.suggestions { + suggestions.push(suggestion); + } + } + /// Show a suggestion that has multiple parts to it. /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion( @@ -340,7 +360,7 @@ impl Diagnostic { style: SuggestionStyle, ) -> &mut Self { assert!(!suggestion.is_empty()); - self.suggestions.push(CodeSuggestion { + self.push_suggestion(CodeSuggestion { substitutions: vec![Substitution { parts: suggestion .into_iter() @@ -368,7 +388,7 @@ impl Diagnostic { applicability: Applicability, ) -> &mut Self { assert!(!suggestion.is_empty()); - self.suggestions.push(CodeSuggestion { + self.push_suggestion(CodeSuggestion { substitutions: vec![Substitution { parts: suggestion .into_iter() @@ -426,7 +446,7 @@ impl Diagnostic { applicability: Applicability, style: SuggestionStyle, ) -> &mut Self { - self.suggestions.push(CodeSuggestion { + self.push_suggestion(CodeSuggestion { substitutions: vec![Substitution { parts: vec![SubstitutionPart { snippet: suggestion, span: sp }], }], @@ -471,7 +491,7 @@ impl Diagnostic { .into_iter() .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }) .collect(); - self.suggestions.push(CodeSuggestion { + self.push_suggestion(CodeSuggestion { substitutions, msg: msg.to_owned(), style: SuggestionStyle::ShowCode, @@ -489,7 +509,7 @@ impl Diagnostic { suggestions: impl Iterator>, applicability: Applicability, ) -> &mut Self { - self.suggestions.push(CodeSuggestion { + self.push_suggestion(CodeSuggestion { substitutions: suggestions .map(|sugg| Substitution { parts: sugg @@ -578,7 +598,7 @@ impl Diagnostic { applicability: Applicability, tool_metadata: Json, ) { - self.suggestions.push(CodeSuggestion { + self.push_suggestion(CodeSuggestion { substitutions: vec![], msg: msg.to_owned(), style: SuggestionStyle::CompletelyHidden, @@ -668,7 +688,7 @@ impl Diagnostic { &Vec<(String, Style)>, &Option, &MultiSpan, - &Vec, + &Result, SuggestionsDisabled>, Option<&Vec>, ) { ( diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 6f84b0d400..3c8751a7a3 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -15,19 +15,14 @@ use tracing::debug; /// extending `HandlerFlags`, accessed via `self.handler.flags`. #[must_use] #[derive(Clone)] -pub struct DiagnosticBuilder<'a>(Box>); - -/// This is a large type, and often used as a return value, especially within -/// the frequently-used `PResult` type. In theory, return value optimization -/// (RVO) should avoid unnecessary copying. In practice, it does not (at the -/// time of writing). The split between `DiagnosticBuilder` and -/// `DiagnosticBuilderInner` exists to avoid many `memcpy` calls. -#[must_use] -#[derive(Clone)] -struct DiagnosticBuilderInner<'a> { +pub struct DiagnosticBuilder<'a> { handler: &'a Handler, - diagnostic: Diagnostic, - allow_suggestions: bool, + + /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a + /// return value, especially within the frequently-used `PResult` type. + /// In theory, return value optimization (RVO) should avoid unnecessary + /// copying. In practice, it does not (at the time of writing). + diagnostic: Box, } /// In general, the `DiagnosticBuilder` uses deref to allow access to @@ -60,7 +55,7 @@ macro_rules! forward { $(#[$attrs])* #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { - self.0.diagnostic.$n($($name),*); + self.diagnostic.$n($($name),*); self } }; @@ -77,7 +72,7 @@ macro_rules! forward { $(#[$attrs])* #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self { - self.0.diagnostic.$n($($name),*); + self.diagnostic.$n($($name),*); self } }; @@ -87,20 +82,20 @@ impl<'a> Deref for DiagnosticBuilder<'a> { type Target = Diagnostic; fn deref(&self) -> &Diagnostic { - &self.0.diagnostic + &self.diagnostic } } impl<'a> DerefMut for DiagnosticBuilder<'a> { fn deref_mut(&mut self) -> &mut Diagnostic { - &mut self.0.diagnostic + &mut self.diagnostic } } impl<'a> DiagnosticBuilder<'a> { /// Emit the diagnostic. pub fn emit(&mut self) { - self.0.handler.emit_diagnostic(&self); + self.handler.emit_diagnostic(&self); self.cancel(); } @@ -130,19 +125,19 @@ impl<'a> DiagnosticBuilder<'a> { /// Converts the builder to a `Diagnostic` for later emission, /// unless handler has disabled such buffering. pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> { - if self.0.handler.flags.dont_buffer_diagnostics - || self.0.handler.flags.treat_err_as_bug.is_some() + if self.handler.flags.dont_buffer_diagnostics + || self.handler.flags.treat_err_as_bug.is_some() { self.emit(); return None; } - let handler = self.0.handler; + let handler = self.handler; // We must use `Level::Cancelled` for `dummy` to avoid an ICE about an // unused diagnostic. let dummy = Diagnostic::new(Level::Cancelled, ""); - let diagnostic = std::mem::replace(&mut self.0.diagnostic, dummy); + let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy); // Logging here is useful to help track down where in logs an error was // actually emitted. @@ -169,7 +164,7 @@ impl<'a> DiagnosticBuilder<'a> { /// locally in whichever way makes the most sense. pub fn delay_as_bug(&mut self) { self.level = Level::Bug; - self.0.handler.delay_as_bug(self.0.diagnostic.clone()); + self.handler.delay_as_bug((*self.diagnostic).clone()); self.cancel(); } @@ -186,7 +181,7 @@ impl<'a> DiagnosticBuilder<'a> { /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is /// primary. pub fn span_label(&mut self, span: Span, label: impl Into) -> &mut Self { - self.0.diagnostic.span_label(span, label); + self.diagnostic.span_label(span, label); self } @@ -199,7 +194,7 @@ impl<'a> DiagnosticBuilder<'a> { ) -> &mut Self { let label = label.as_ref(); for span in spans { - self.0.diagnostic.span_label(span, label); + self.diagnostic.span_label(span, label); } self } @@ -244,164 +239,79 @@ impl<'a> DiagnosticBuilder<'a> { ) -> &mut Self); forward!(pub fn set_is_lint(&mut self,) -> &mut Self); - /// See [`Diagnostic::multipart_suggestion()`]. - pub fn multipart_suggestion( + forward!(pub fn disable_suggestions(&mut self,) -> &mut Self); + + forward!(pub fn multipart_suggestion( &mut self, msg: &str, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.multipart_suggestion(msg, suggestion, applicability); - self - } - - /// See [`Diagnostic::multipart_suggestion()`]. - pub fn multipart_suggestion_verbose( + ) -> &mut Self); + forward!(pub fn multipart_suggestion_verbose( &mut self, msg: &str, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.multipart_suggestion_verbose(msg, suggestion, applicability); - self - } - - /// See [`Diagnostic::tool_only_multipart_suggestion()`]. - pub fn tool_only_multipart_suggestion( + ) -> &mut Self); + forward!(pub fn tool_only_multipart_suggestion( &mut self, msg: &str, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.tool_only_multipart_suggestion(msg, suggestion, applicability); - self - } - - /// See [`Diagnostic::span_suggestion()`]. - pub fn span_suggestion( + ) -> &mut Self); + forward!(pub fn span_suggestion( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.span_suggestion(sp, msg, suggestion, applicability); - self - } - - /// See [`Diagnostic::span_suggestions()`]. - pub fn span_suggestions( + ) -> &mut Self); + forward!(pub fn span_suggestions( &mut self, sp: Span, msg: &str, suggestions: impl Iterator, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.span_suggestions(sp, msg, suggestions, applicability); - self - } - - /// See [`Diagnostic::multipart_suggestions()`]. - pub fn multipart_suggestions( + ) -> &mut Self); + forward!(pub fn multipart_suggestions( &mut self, msg: &str, suggestions: impl Iterator>, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.multipart_suggestions(msg, suggestions, applicability); - self - } - - /// See [`Diagnostic::span_suggestion_short()`]. - pub fn span_suggestion_short( + ) -> &mut Self); + forward!(pub fn span_suggestion_short( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.span_suggestion_short(sp, msg, suggestion, applicability); - self - } - - /// See [`Diagnostic::span_suggestion_verbose()`]. - pub fn span_suggestion_verbose( + ) -> &mut Self); + forward!(pub fn span_suggestion_verbose( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.span_suggestion_verbose(sp, msg, suggestion, applicability); - self - } - - /// See [`Diagnostic::span_suggestion_hidden()`]. - pub fn span_suggestion_hidden( + ) -> &mut Self); + forward!(pub fn span_suggestion_hidden( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.span_suggestion_hidden(sp, msg, suggestion, applicability); - self - } - - /// See [`Diagnostic::tool_only_span_suggestion()`] for more information. - pub fn tool_only_span_suggestion( + ) -> &mut Self); + forward!(pub fn tool_only_span_suggestion( &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.tool_only_span_suggestion(sp, msg, suggestion, applicability); - self - } + ) -> &mut 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); - /// Allow attaching suggestions this diagnostic. - /// If this is set to `false`, then any suggestions attached with the `span_suggestion_*` - /// methods after this is set to `false` will be ignored. - pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self { - self.0.allow_suggestions = allow; - self - } - /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { @@ -424,17 +334,13 @@ impl<'a> DiagnosticBuilder<'a> { /// diagnostic. crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> { debug!("Created new diagnostic"); - DiagnosticBuilder(Box::new(DiagnosticBuilderInner { - handler, - diagnostic, - allow_suggestions: true, - })) + DiagnosticBuilder { handler, diagnostic: Box::new(diagnostic) } } } impl<'a> Debug for DiagnosticBuilder<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.diagnostic.fmt(f) + self.diagnostic.fmt(f) } } @@ -444,7 +350,7 @@ impl<'a> Drop for DiagnosticBuilder<'a> { fn drop(&mut self) { if !panicking() && !self.cancelled() { let mut db = DiagnosticBuilder::new( - self.0.handler, + self.handler, Level::Bug, "the following error was constructed but not emitted", ); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 3104bc185e..f90f4d46a9 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -227,7 +227,8 @@ pub trait Emitter { diag: &'a Diagnostic, ) -> (MultiSpan, &'a [CodeSuggestion]) { let mut primary_span = diag.span.clone(); - if let Some((sugg, rest)) = diag.suggestions.split_first() { + let suggestions = diag.suggestions.as_ref().map_or(&[][..], |suggestions| &suggestions[..]); + if let Some((sugg, rest)) = suggestions.split_first() { if rest.is_empty() && // ^ if there is only one suggestion // don't display multi-suggestions as labels @@ -282,10 +283,10 @@ pub trait Emitter { // to be consistent. We could try to figure out if we can // make one (or the first one) inline, but that would give // undue importance to a semi-random suggestion - (primary_span, &diag.suggestions) + (primary_span, suggestions) } } else { - (primary_span, &diag.suggestions) + (primary_span, suggestions) } } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index dde978cd8c..ff3478073d 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -345,7 +345,7 @@ struct UnusedExterns<'a, 'b, 'c> { impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { - let sugg = diag.suggestions.iter().map(|sugg| Diagnostic { + let sugg = diag.suggestions.iter().flatten().map(|sugg| Diagnostic { message: sugg.msg.clone(), code: None, level: "help", @@ -455,7 +455,7 @@ impl DiagnosticSpan { let backtrace_step = backtrace.next().map(|bt| { let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); let def_site_span = - Self::from_span_full(bt.def_site, false, None, None, vec![].into_iter(), je); + Self::from_span_full(bt.def_site, false, None, None, [].into_iter(), je); Box::new(DiagnosticSpanMacroExpansion { span: call_site, macro_decl_name: bt.kind.descr(), diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index d055937ac3..c5b3d20440 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -64,7 +64,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { let bytes = output.lock().unwrap(); let actual_output = str::from_utf8(&bytes).unwrap(); - let actual_output: TestData = decode(actual_output).unwrap(); + let actual_output: TestData = decode(actual_output); assert_eq!(expected_output, actual_output) }) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a681298301..a5c954cca1 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -8,6 +8,7 @@ #![feature(if_let_guard)] #![feature(let_else)] #![feature(nll)] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_macros; @@ -54,9 +55,11 @@ pub use snippet::Style; pub type PResult<'a, T> = Result>; // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. -// (See also the comment on `DiagnosticBuilderInner`.) +// (See also the comment on `DiagnosticBuilder`'s `diagnostic` field.) #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); +rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24); #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] pub enum SuggestionStyle { @@ -99,8 +102,8 @@ impl Hash for ToolMetadata { // Doesn't really need to round-trip impl Decodable for ToolMetadata { - fn decode(_d: &mut D) -> Result { - Ok(ToolMetadata(None)) + fn decode(_d: &mut D) -> Self { + ToolMetadata(None) } } @@ -445,9 +448,6 @@ struct HandlerInner { deduplicated_warn_count: usize, future_breakage_diagnostics: Vec, - - /// If set to `true`, no warning or error will be emitted. - quiet: bool, } /// A key denoting where from a diagnostic was stashed. @@ -563,19 +563,10 @@ impl Handler { emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), future_breakage_diagnostics: Vec::new(), - quiet: false, }), } } - pub fn with_disabled_diagnostic T>(&self, f: F) -> T { - let prev = self.inner.borrow_mut().quiet; - self.inner.borrow_mut().quiet = true; - let ret = f(); - self.inner.borrow_mut().quiet = prev; - ret - } - // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { @@ -946,7 +937,7 @@ impl HandlerInner { } fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) { - if diagnostic.cancelled() || self.quiet { + if diagnostic.cancelled() { return; } @@ -1170,9 +1161,6 @@ impl HandlerInner { } fn delay_as_bug(&mut self, diagnostic: Diagnostic) { - if self.quiet { - return; - } if self.flags.report_delayed_bugs { self.emit_diagnostic(&diagnostic); } diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index 64353461e9..e4cc44c41d 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -69,9 +69,6 @@ pub enum AnnotationType { /// Annotation under a single line of code Singleline, - /// Annotation enclosing the first and last character of a multiline span - Multiline(MultilineAnnotation), - // The Multiline type above is replaced with the following three in order // to reuse the current label drawing code. // diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 07b5e20b2d..258320aeb6 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -8,7 +8,7 @@ use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; @@ -920,8 +920,25 @@ pub trait ResolverExpand { /// we generated proc macros harnesses, so that we can map /// HIR proc macros items back to their harness items. fn declare_proc_macro(&mut self, id: NodeId); + + /// Tools registered with `#![register_tool]` and used by tool attributes and lints. + fn registered_tools(&self) -> &FxHashSet; } +pub trait LintStoreExpand { + fn pre_expansion_lint( + &self, + sess: &Session, + registered_tools: &FxHashSet, + node_id: NodeId, + attrs: &[Attribute], + items: &[P], + name: &str, + ); +} + +type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>; + #[derive(Clone, Default)] pub struct ModuleData { /// Path to the module starting from the crate name, like `my_crate::foo::bar`. @@ -956,9 +973,6 @@ pub struct ExpansionData { pub is_trailing_mac: bool, } -type OnExternModLoaded<'a> = - Option<&'a dyn Fn(Ident, Vec, Vec>, Span) -> (Vec, Vec>)>; - /// One of these is made during expansion and incrementally updated as we go; /// when a macro expansion occurs, the resulting nodes have the `backtrace() /// -> expn_data` of their expansion context stored into their span. @@ -973,10 +987,8 @@ pub struct ExtCtxt<'a> { /// (or during eager expansion, but that's a hack). pub force_mode: bool, pub expansions: FxHashMap>, - /// Called directly after having parsed an external `mod foo;` in expansion. - /// - /// `Ident` is the module name. - pub(super) extern_mod_loaded: OnExternModLoaded<'a>, + /// Used for running pre-expansion lints on freshly loaded modules. + pub(super) lint_store: LintStoreExpandDyn<'a>, /// When we 'expand' an inert attribute, we leave it /// in the AST, but insert it here so that we know /// not to expand it again. @@ -988,14 +1000,14 @@ impl<'a> ExtCtxt<'a> { sess: &'a Session, ecfg: expand::ExpansionConfig<'a>, resolver: &'a mut dyn ResolverExpand, - extern_mod_loaded: OnExternModLoaded<'a>, + lint_store: LintStoreExpandDyn<'a>, ) -> ExtCtxt<'a> { ExtCtxt { sess, ecfg, reduced_recursion_limit: None, resolver, - extern_mod_loaded, + lint_store, root_path: PathBuf::new(), current_expansion: ExpansionData { id: LocalExpnId::ROOT, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index db0dea4870..5fa7ffd554 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -238,7 +238,7 @@ macro_rules! configure { } impl<'a> StripUnconfigured<'a> { - pub fn configure(&mut self, mut node: T) -> Option { + pub fn configure(&self, mut node: T) -> Option { self.process_cfg_attrs(&mut node); if self.in_cfg(node.attrs()) { self.try_configure_tokens(&mut node); @@ -248,7 +248,7 @@ impl<'a> StripUnconfigured<'a> { } } - fn try_configure_tokens(&mut self, node: &mut T) { + fn try_configure_tokens(&self, node: &mut T) { if self.config_tokens { if let Some(Some(tokens)) = node.tokens_mut() { let attr_annotated_tokens = tokens.create_token_stream(); @@ -257,10 +257,7 @@ impl<'a> StripUnconfigured<'a> { } } - fn configure_krate_attrs( - &mut self, - mut attrs: Vec, - ) -> Option> { + fn configure_krate_attrs(&self, mut attrs: Vec) -> Option> { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); if self.in_cfg(&attrs) { Some(attrs) } else { None } } @@ -269,7 +266,7 @@ impl<'a> StripUnconfigured<'a> { /// This is only used during the invocation of `derive` proc-macros, /// which require that we cfg-expand their entire input. /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method - fn configure_tokens(&mut self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { + fn configure_tokens(&self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { fn can_skip(stream: &AttrAnnotatedTokenStream) -> bool { stream.0.iter().all(|(tree, _spacing)| match tree { AttrAnnotatedTokenTree::Attributes(_) => false, @@ -325,12 +322,16 @@ impl<'a> StripUnconfigured<'a> { /// Gives compiler warnings if any `cfg_attr` does not contain any /// attributes and is in the original source code. Gives compiler errors if /// the syntax of any `cfg_attr` is incorrect. - fn process_cfg_attrs(&mut self, node: &mut T) { + fn process_cfg_attrs(&self, node: &mut T) { node.visit_attrs(|attrs| { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); }); } + fn process_cfg_attr(&self, attr: Attribute) -> Vec { + if attr.has_name(sym::cfg_attr) { self.expand_cfg_attr(attr, true) } else { vec![attr] } + } + /// Parse and expand a single `cfg_attr` attribute into a list of attributes /// when the configuration predicate is true, or otherwise expand into an /// empty list of attributes. @@ -338,11 +339,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. - fn process_cfg_attr(&mut self, attr: Attribute) -> Vec { - if !attr.has_name(sym::cfg_attr) { - return vec![attr]; - } - + crate fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec { let (cfg_predicate, expanded_attrs) = match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) { None => return vec![], @@ -351,95 +348,109 @@ impl<'a> StripUnconfigured<'a> { // Lint on zero attributes in source. if expanded_attrs.is_empty() { - return vec![attr]; + self.sess.parse_sess.buffer_lint( + rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, + attr.span, + ast::CRATE_NODE_ID, + "`#[cfg_attr]` does not expand to any attributes", + ); } if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) { return vec![]; } - // We call `process_cfg_attr` recursively in case there's a - // `cfg_attr` inside of another `cfg_attr`. E.g. - // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. - expanded_attrs - .into_iter() - .flat_map(|(item, span)| { - let orig_tokens = attr.tokens().to_tokenstream(); - - // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` - // and producing an attribute of the form `#[attr]`. We - // have captured tokens for `attr` itself, but we need to - // synthesize tokens for the wrapper `#` and `[]`, which - // we do below. - - // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token - // for `attr` when we expand it to `#[attr]` - let mut orig_trees = orig_tokens.trees(); - let pound_token = match orig_trees.next().unwrap() { - TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token, - _ => panic!("Bad tokens for attribute {:?}", attr), - }; - let pound_span = pound_token.span; - - let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; - if attr.style == AttrStyle::Inner { - // For inner attributes, we do the same thing for the `!` in `#![some_attr]` - let bang_token = match orig_trees.next().unwrap() { - TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token, - _ => panic!("Bad tokens for attribute {:?}", attr), - }; - trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); - } - // We don't really have a good span to use for the syntheized `[]` - // in `#[attr]`, so just use the span of the `#` token. - let bracket_group = AttrAnnotatedTokenTree::Delimited( - DelimSpan::from_single(pound_span), - DelimToken::Bracket, - item.tokens - .as_ref() - .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) - .create_token_stream(), - ); - trees.push((bracket_group, Spacing::Alone)); - let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees))); - let attr = attr::mk_attr_from_item(item, tokens, attr.style, span); - if attr.has_name(sym::crate_type) { - self.sess.parse_sess.buffer_lint( - rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, - attr.span, - ast::CRATE_NODE_ID, - "`crate_type` within an `#![cfg_attr] attribute is deprecated`", - ); - } - if attr.has_name(sym::crate_name) { - self.sess.parse_sess.buffer_lint( - rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, - attr.span, - ast::CRATE_NODE_ID, - "`crate_name` within an `#![cfg_attr] attribute is deprecated`", - ); - } - self.process_cfg_attr(attr) - }) - .collect() + if recursive { + // We call `process_cfg_attr` recursively in case there's a + // `cfg_attr` inside of another `cfg_attr`. E.g. + // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. + expanded_attrs + .into_iter() + .flat_map(|item| self.process_cfg_attr(self.expand_cfg_attr_item(&attr, item))) + .collect() + } else { + expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(&attr, item)).collect() + } + } + + fn expand_cfg_attr_item( + &self, + attr: &Attribute, + (item, item_span): (ast::AttrItem, Span), + ) -> Attribute { + let orig_tokens = attr.tokens().to_tokenstream(); + + // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` + // and producing an attribute of the form `#[attr]`. We + // have captured tokens for `attr` itself, but we need to + // synthesize tokens for the wrapper `#` and `[]`, which + // we do below. + + // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token + // for `attr` when we expand it to `#[attr]` + let mut orig_trees = orig_tokens.trees(); + let pound_token = match orig_trees.next().unwrap() { + TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token, + _ => panic!("Bad tokens for attribute {:?}", attr), + }; + let pound_span = pound_token.span; + + let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; + if attr.style == AttrStyle::Inner { + // For inner attributes, we do the same thing for the `!` in `#![some_attr]` + let bang_token = match orig_trees.next().unwrap() { + TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token, + _ => panic!("Bad tokens for attribute {:?}", attr), + }; + trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); + } + // We don't really have a good span to use for the syntheized `[]` + // in `#[attr]`, so just use the span of the `#` token. + let bracket_group = AttrAnnotatedTokenTree::Delimited( + DelimSpan::from_single(pound_span), + DelimToken::Bracket, + item.tokens + .as_ref() + .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) + .create_token_stream(), + ); + trees.push((bracket_group, Spacing::Alone)); + let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees))); + let attr = attr::mk_attr_from_item(item, tokens, attr.style, item_span); + if attr.has_name(sym::crate_type) { + self.sess.parse_sess.buffer_lint( + rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, + attr.span, + ast::CRATE_NODE_ID, + "`crate_type` within an `#![cfg_attr] attribute is deprecated`", + ); + } + if attr.has_name(sym::crate_name) { + self.sess.parse_sess.buffer_lint( + rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, + attr.span, + ast::CRATE_NODE_ID, + "`crate_name` within an `#![cfg_attr] attribute is deprecated`", + ); + } + attr } /// Determines if a node with the given attributes should be included in this configuration. fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| { - if !is_cfg(attr) { + attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr)) + } + + crate fn cfg_true(&self, attr: &Attribute) -> bool { + let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { + Ok(meta_item) => meta_item, + Err(mut err) => { + err.emit(); return true; } - let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { - Ok(meta_item) => meta_item, - Err(mut err) => { - err.emit(); - return true; - } - }; - parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { - attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features) - }) + }; + parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { + attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features) }) } @@ -461,7 +472,7 @@ impl<'a> StripUnconfigured<'a> { } } - pub fn configure_expr(&mut self, expr: &mut P) { + pub fn configure_expr(&self, expr: &mut P) { for attr in expr.attrs.iter() { self.maybe_emit_expr_attr_err(attr); } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 7f49f80a84..9a4daa6d75 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,6 +1,5 @@ use crate::base::*; use crate::config::StripUnconfigured; -use crate::configure; use crate::hygiene::SyntaxContext; use crate::mbe::macro_rules::annotate_err_with_kind; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; @@ -12,13 +11,11 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; -use rustc_ast::{AstLike, Block, Inline, ItemKind, MacArgs, MacCall}; -use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; -use rustc_ast::{NodeId, PatKind, Path, StmtKind}; +use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind}; +use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; +use rustc_ast::{NodeId, PatKind, StmtKind, TyKind}; use rustc_ast_pretty::pprust; -use rustc_attr::is_builtin_attr; use rustc_data_structures::map_in_place::MapInPlace; -use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, PResult}; use rustc_feature::Features; @@ -34,7 +31,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::{FileName, LocalExpnId, Span}; use smallvec::SmallVec; -use std::ops::DerefMut; +use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; use std::{iter, mem}; @@ -109,6 +106,10 @@ macro_rules! ast_fragments { } })* + fn make_ast(self) -> T::OutputTy { + T::fragment_to_output(self) + } + pub fn mut_visit_with(&mut self, vis: &mut F) { match self { AstFragment::OptExpr(opt_expr) => { @@ -178,10 +179,10 @@ ast_fragments! { Arms(SmallVec<[ast::Arm; 1]>) { "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms; } - Fields(SmallVec<[ast::ExprField; 1]>) { + ExprFields(SmallVec<[ast::ExprField; 1]>) { "field expression"; many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields; } - FieldPats(SmallVec<[ast::PatField; 1]>) { + PatFields(SmallVec<[ast::PatField; 1]>) { "field pattern"; many fn flat_map_pat_field; fn visit_pat_field(); @@ -196,7 +197,7 @@ ast_fragments! { Params(SmallVec<[ast::Param; 1]>) { "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params; } - StructFields(SmallVec<[ast::FieldDef; 1]>) { + FieldDefs(SmallVec<[ast::FieldDef; 1]>) { "field"; many fn flat_map_field_def; fn visit_field_def(); @@ -231,11 +232,11 @@ impl AstFragmentKind { | AstFragmentKind::ForeignItems | AstFragmentKind::Crate => SupportsMacroExpansion::Yes { supports_inner_attrs: true }, AstFragmentKind::Arms - | AstFragmentKind::Fields - | AstFragmentKind::FieldPats + | AstFragmentKind::ExprFields + | AstFragmentKind::PatFields | AstFragmentKind::GenericParams | AstFragmentKind::Params - | AstFragmentKind::StructFields + | AstFragmentKind::FieldDefs | AstFragmentKind::Variants => SupportsMacroExpansion::No, } } @@ -249,11 +250,11 @@ impl AstFragmentKind { AstFragmentKind::Arms => { AstFragment::Arms(items.map(Annotatable::expect_arm).collect()) } - AstFragmentKind::Fields => { - AstFragment::Fields(items.map(Annotatable::expect_expr_field).collect()) + AstFragmentKind::ExprFields => { + AstFragment::ExprFields(items.map(Annotatable::expect_expr_field).collect()) } - AstFragmentKind::FieldPats => { - AstFragment::FieldPats(items.map(Annotatable::expect_pat_field).collect()) + AstFragmentKind::PatFields => { + AstFragment::PatFields(items.map(Annotatable::expect_pat_field).collect()) } AstFragmentKind::GenericParams => { AstFragment::GenericParams(items.map(Annotatable::expect_generic_param).collect()) @@ -261,8 +262,8 @@ impl AstFragmentKind { AstFragmentKind::Params => { AstFragment::Params(items.map(Annotatable::expect_param).collect()) } - AstFragmentKind::StructFields => { - AstFragment::StructFields(items.map(Annotatable::expect_field_def).collect()) + AstFragmentKind::FieldDefs => { + AstFragment::FieldDefs(items.map(Annotatable::expect_field_def).collect()) } AstFragmentKind::Variants => { AstFragment::Variants(items.map(Annotatable::expect_variant).collect()) @@ -315,10 +316,10 @@ pub enum InvocationKind { pos: usize, item: Annotatable, // Required for resolving derive helper attributes. - derives: Vec, + derives: Vec, }, Derive { - path: Path, + path: ast::Path, item: Annotatable, }, } @@ -676,7 +677,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { krate, ), Annotatable::Item(item_inner) - if matches!(attr.style, ast::AttrStyle::Inner) + if matches!(attr.style, AttrStyle::Inner) && matches!( item_inner.kind, ItemKind::Mod( @@ -744,7 +745,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } - let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path }; + let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path }; let items = match expander.expand(self.cx, span, &meta, item) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { @@ -806,7 +807,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> { fn visit_item(&mut self, item: &'ast ast::Item) { match &item.kind { - ast::ItemKind::Mod(_, mod_kind) + ItemKind::Mod(_, mod_kind) if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) => { feature_err( @@ -834,7 +835,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { &mut self, toks: TokenStream, kind: AstFragmentKind, - path: &Path, + path: &ast::Path, span: Span, ) -> AstFragment { let mut parser = self.cx.new_parser_from_tts(toks); @@ -915,18 +916,18 @@ pub fn parse_ast_fragment<'a>( )?), AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?), AstFragmentKind::Arms - | AstFragmentKind::Fields - | AstFragmentKind::FieldPats + | AstFragmentKind::ExprFields + | AstFragmentKind::PatFields | AstFragmentKind::GenericParams | AstFragmentKind::Params - | AstFragmentKind::StructFields + | AstFragmentKind::FieldDefs | AstFragmentKind::Variants => panic!("unexpected AST fragment kind"), }) } pub fn ensure_complete_parse<'a>( this: &mut Parser<'a>, - macro_path: &Path, + macro_path: &ast::Path, kind_name: &str, span: Span, ) { @@ -961,6 +962,581 @@ pub fn ensure_complete_parse<'a>( } } +/// Wraps a call to `noop_visit_*` / `noop_flat_map_*` +/// for an AST node that supports attributes +/// (see the `Annotatable` enum) +/// This method assigns a `NodeId`, and sets that `NodeId` +/// as our current 'lint node id'. If a macro call is found +/// inside this AST node, we will use this AST node's `NodeId` +/// to emit lints associated with that macro (allowing +/// `#[allow]` / `#[deny]` to be applied close to +/// the macro invocation). +/// +/// Do *not* call this for a macro AST node +/// (e.g. `ExprKind::MacCall`) - we cannot emit lints +/// at these AST nodes, since they are removed and +/// replaced with the result of macro expansion. +/// +/// All other `NodeId`s are assigned by `visit_id`. +/// * `self` is the 'self' parameter for the current method, +/// * `id` is a mutable reference to the `NodeId` field +/// of the current AST node. +/// * `closure` is a closure that executes the +/// `noop_visit_*` / `noop_flat_map_*` method +/// for the current AST node. +macro_rules! assign_id { + ($self:ident, $id:expr, $closure:expr) => {{ + let old_id = $self.cx.current_expansion.lint_node_id; + if $self.monotonic { + debug_assert_eq!(*$id, ast::DUMMY_NODE_ID); + let new_id = $self.cx.resolver.next_node_id(); + *$id = new_id; + $self.cx.current_expansion.lint_node_id = new_id; + } + let ret = ($closure)(); + $self.cx.current_expansion.lint_node_id = old_id; + ret + }}; +} + +enum AddSemicolon { + Yes, + No, +} + +/// A trait implemented for all `AstFragment` nodes and providing all pieces +/// of functionality used by `InvocationCollector`. +trait InvocationCollectorNode: AstLike { + type OutputTy = SmallVec<[Self; 1]>; + type AttrsTy: Deref = Vec; + const KIND: AstFragmentKind; + fn to_annotatable(self) -> Annotatable; + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy; + fn id(&mut self) -> &mut NodeId; + fn descr() -> &'static str { + unreachable!() + } + fn noop_flat_map(self, _visitor: &mut V) -> Self::OutputTy { + unreachable!() + } + fn noop_visit(&mut self, _visitor: &mut V) { + unreachable!() + } + fn is_mac_call(&self) -> bool { + false + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + unreachable!() + } + fn pre_flat_map_node_collect_attr(_cfg: &StripUnconfigured<'_>, _attr: &ast::Attribute) {} + fn post_flat_map_node_collect_bang(_output: &mut Self::OutputTy, _add_semicolon: AddSemicolon) { + } + fn wrap_flat_map_node_noop_flat_map( + node: Self, + collector: &mut InvocationCollector<'_, '_>, + noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy, + ) -> Result { + Ok(noop_flat_map(node, collector)) + } +} + +impl InvocationCollectorNode for P { + const KIND: AstFragmentKind = AstFragmentKind::Items; + fn to_annotatable(self) -> Annotatable { + Annotatable::Item(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_item(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } + fn wrap_flat_map_node_noop_flat_map( + mut node: Self, + collector: &mut InvocationCollector<'_, '_>, + noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy, + ) -> Result { + if !matches!(node.kind, ItemKind::Mod(..)) { + return Ok(noop_flat_map(node, collector)); + } + + // Work around borrow checker not seeing through `P`'s deref. + let (ident, span, mut attrs) = (node.ident, node.span, mem::take(&mut node.attrs)); + let ItemKind::Mod(_, mod_kind) = &mut node.kind else { + unreachable!() + }; + + let ecx = &mut collector.cx; + let (file_path, dir_path, dir_ownership) = match mod_kind { + ModKind::Loaded(_, inline, _) => { + // Inline `mod foo { ... }`, but we still need to push directories. + let (dir_path, dir_ownership) = mod_dir_path( + &ecx.sess, + ident, + &attrs, + &ecx.current_expansion.module, + ecx.current_expansion.dir_ownership, + *inline, + ); + node.attrs = attrs; + (None, dir_path, dir_ownership) + } + ModKind::Unloaded => { + // We have an outline `mod foo;` so we need to parse the file. + let old_attrs_len = attrs.len(); + let ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership } = + parse_external_mod( + &ecx.sess, + ident, + span, + &ecx.current_expansion.module, + ecx.current_expansion.dir_ownership, + &mut attrs, + ); + + if let Some(lint_store) = ecx.lint_store { + lint_store.pre_expansion_lint( + ecx.sess, + ecx.resolver.registered_tools(), + ecx.current_expansion.lint_node_id, + &attrs, + &items, + ident.name.as_str(), + ); + } + + *mod_kind = ModKind::Loaded(items, Inline::No, inner_span); + node.attrs = attrs; + if node.attrs.len() > old_attrs_len { + // If we loaded an out-of-line module and added some inner attributes, + // then we need to re-configure it and re-collect attributes for + // resolution and expansion. + return Err(node); + } + (Some(file_path), dir_path, dir_ownership) + } + }; + + // Set the module info before we flat map. + let mut module = ecx.current_expansion.module.with_dir_path(dir_path); + module.mod_path.push(ident); + if let Some(file_path) = file_path { + module.file_path_stack.push(file_path); + } + + let orig_module = mem::replace(&mut ecx.current_expansion.module, Rc::new(module)); + let orig_dir_ownership = + mem::replace(&mut ecx.current_expansion.dir_ownership, dir_ownership); + + let res = Ok(noop_flat_map(node, collector)); + + collector.cx.current_expansion.dir_ownership = orig_dir_ownership; + collector.cx.current_expansion.module = orig_module; + res + } +} + +struct TraitItemTag; +impl InvocationCollectorNode for AstLikeWrapper, TraitItemTag> { + type OutputTy = SmallVec<[P; 1]>; + const KIND: AstFragmentKind = AstFragmentKind::TraitItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::TraitItem(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_trait_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_assoc_item(self.wrapped, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let item = self.wrapped.into_inner(); + match item.kind { + AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +struct ImplItemTag; +impl InvocationCollectorNode for AstLikeWrapper, ImplItemTag> { + type OutputTy = SmallVec<[P; 1]>; + const KIND: AstFragmentKind = AstFragmentKind::ImplItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::ImplItem(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_impl_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_assoc_item(self.wrapped, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let item = self.wrapped.into_inner(); + match item.kind { + AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P { + const KIND: AstFragmentKind = AstFragmentKind::ForeignItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::ForeignItem(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_foreign_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_foreign_item(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ForeignItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ForeignItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for ast::Variant { + const KIND: AstFragmentKind = AstFragmentKind::Variants; + fn to_annotatable(self) -> Annotatable { + Annotatable::Variant(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_variants() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_variant(self, visitor) + } +} + +impl InvocationCollectorNode for ast::FieldDef { + const KIND: AstFragmentKind = AstFragmentKind::FieldDefs; + fn to_annotatable(self) -> Annotatable { + Annotatable::FieldDef(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_field_defs() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_field_def(self, visitor) + } +} + +impl InvocationCollectorNode for ast::PatField { + const KIND: AstFragmentKind = AstFragmentKind::PatFields; + fn to_annotatable(self) -> Annotatable { + Annotatable::PatField(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_pat_fields() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_pat_field(self, visitor) + } +} + +impl InvocationCollectorNode for ast::ExprField { + const KIND: AstFragmentKind = AstFragmentKind::ExprFields; + fn to_annotatable(self) -> Annotatable { + Annotatable::ExprField(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_expr_fields() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_expr_field(self, visitor) + } +} + +impl InvocationCollectorNode for ast::Param { + const KIND: AstFragmentKind = AstFragmentKind::Params; + fn to_annotatable(self) -> Annotatable { + Annotatable::Param(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_params() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_param(self, visitor) + } +} + +impl InvocationCollectorNode for ast::GenericParam { + const KIND: AstFragmentKind = AstFragmentKind::GenericParams; + fn to_annotatable(self) -> Annotatable { + Annotatable::GenericParam(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_generic_params() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_generic_param(self, visitor) + } +} + +impl InvocationCollectorNode for ast::Arm { + const KIND: AstFragmentKind = AstFragmentKind::Arms; + fn to_annotatable(self) -> Annotatable { + Annotatable::Arm(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_arms() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_arm(self, visitor) + } +} + +impl InvocationCollectorNode for ast::Stmt { + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::Stmts; + fn to_annotatable(self) -> Annotatable { + Annotatable::Stmt(P(self)) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_stmts() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_stmt(self, visitor) + } + fn is_mac_call(&self) -> bool { + match &self.kind { + StmtKind::MacCall(..) => true, + StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)), + StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)), + StmtKind::Expr(..) => unreachable!(), + StmtKind::Local(..) | StmtKind::Empty => false, + } + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + // We pull macro invocations (both attributes and fn-like macro calls) out of their + // `StmtKind`s and treat them as statement macro invocations, not as items or expressions. + let (add_semicolon, mac, attrs) = match self.kind { + StmtKind::MacCall(mac) => { + let ast::MacCallStmt { mac, style, attrs, .. } = mac.into_inner(); + (style == MacStmtStyle::Semicolon, mac, attrs) + } + StmtKind::Item(item) => match item.into_inner() { + ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => { + (mac.args.need_semicolon(), mac, attrs.into()) + } + _ => unreachable!(), + }, + StmtKind::Semi(expr) => match expr.into_inner() { + ast::Expr { kind: ExprKind::MacCall(mac), attrs, .. } => { + (mac.args.need_semicolon(), mac, attrs) + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + (mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No }) + } + fn post_flat_map_node_collect_bang(stmts: &mut Self::OutputTy, add_semicolon: AddSemicolon) { + // If this is a macro invocation with a semicolon, then apply that + // semicolon to the final statement produced by expansion. + if matches!(add_semicolon, AddSemicolon::Yes) { + if let Some(stmt) = stmts.pop() { + stmts.push(stmt.add_trailing_semicolon()); + } + } + } +} + +impl InvocationCollectorNode for ast::Crate { + type OutputTy = ast::Crate; + const KIND: AstFragmentKind = AstFragmentKind::Crate; + fn to_annotatable(self) -> Annotatable { + Annotatable::Crate(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_crate() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit(&mut self, visitor: &mut V) { + noop_visit_crate(self, visitor) + } +} + +impl InvocationCollectorNode for P { + type OutputTy = P; + const KIND: AstFragmentKind = AstFragmentKind::Ty; + fn to_annotatable(self) -> Annotatable { + unreachable!() + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_ty() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit(&mut self, visitor: &mut V) { + noop_visit_ty(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ast::TyKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + TyKind::MacCall(mac) => (mac, Vec::new(), AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P { + type OutputTy = P; + const KIND: AstFragmentKind = AstFragmentKind::Pat; + fn to_annotatable(self) -> Annotatable { + unreachable!() + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_pat() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit(&mut self, visitor: &mut V) { + noop_visit_pat(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, PatKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + PatKind::MacCall(mac) => (mac, Vec::new(), AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P { + type OutputTy = P; + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::Expr; + fn to_annotatable(self) -> Annotatable { + Annotatable::Expr(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_expr() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn descr() -> &'static str { + "an expression" + } + fn noop_visit(&mut self, visitor: &mut V) { + noop_visit_expr(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ExprKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +struct OptExprTag; +impl InvocationCollectorNode for AstLikeWrapper, OptExprTag> { + type OutputTy = Option>; + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::OptExpr; + fn to_annotatable(self) -> Annotatable { + Annotatable::Expr(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_opt_expr() + } + fn id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map(mut self, visitor: &mut V) -> Self::OutputTy { + noop_visit_expr(&mut self.wrapped, visitor); + Some(self.wrapped) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.wrapped.into_inner(); + match node.kind { + ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } + fn pre_flat_map_node_collect_attr(cfg: &StripUnconfigured<'_>, attr: &ast::Attribute) { + cfg.maybe_emit_expr_attr_err(&attr); + } +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, cfg: StripUnconfigured<'a>, @@ -996,7 +1572,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_attr( &mut self, - (attr, pos, derives): (ast::Attribute, usize, Vec), + (attr, pos, derives): (ast::Attribute, usize, Vec), item: Annotatable, kind: AstFragmentKind, ) -> AstFragment { @@ -1007,18 +1583,33 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { /// its position and derives following it. We have to collect the derives in order to resolve /// legacy derive helpers (helpers written before derives that introduce them). fn take_first_attr( - &mut self, + &self, item: &mut impl AstLike, - ) -> Option<(ast::Attribute, usize, Vec)> { + ) -> Option<(ast::Attribute, usize, Vec)> { let mut attr = None; + let mut cfg_pos = None; + let mut attr_pos = None; + for (pos, attr) in item.attrs().iter().enumerate() { + if !attr.is_doc_comment() && !self.cx.expanded_inert_attrs.is_marked(attr) { + let name = attr.ident().map(|ident| ident.name); + if name == Some(sym::cfg) || name == Some(sym::cfg_attr) { + cfg_pos = Some(pos); // a cfg attr found, no need to search anymore + break; + } else if attr_pos.is_none() + && !name.map_or(false, rustc_feature::is_builtin_attr_name) + { + attr_pos = Some(pos); // a non-cfg attr found, still may find a cfg attr + } + } + } + item.visit_attrs(|attrs| { - attr = attrs - .iter() - .position(|a| !self.cx.expanded_inert_attrs.is_marked(a) && !is_builtin_attr(a)) - .map(|attr_pos| { - let attr = attrs.remove(attr_pos); - let following_derives = attrs[attr_pos..] + attr = Some(match (cfg_pos, attr_pos) { + (Some(pos), _) => (attrs.remove(pos), pos, Vec::new()), + (_, Some(pos)) => { + let attr = attrs.remove(pos); + let following_derives = attrs[pos..] .iter() .filter(|a| a.has_name(sym::derive)) .flat_map(|a| a.meta_item_list().unwrap_or_default()) @@ -1032,52 +1623,18 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { }) .collect(); - (attr, attr_pos, following_derives) - }) + (attr, pos, following_derives) + } + _ => return, + }); }); attr } - fn take_stmt_bang( - &mut self, - stmt: ast::Stmt, - ) -> Result<(bool, MacCall, Vec), ast::Stmt> { - match stmt.kind { - StmtKind::MacCall(mac) => { - let MacCallStmt { mac, style, attrs, .. } = mac.into_inner(); - Ok((style == MacStmtStyle::Semicolon, mac, attrs.into())) - } - StmtKind::Item(item) if matches!(item.kind, ItemKind::MacCall(..)) => { - match item.into_inner() { - ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => { - Ok((mac.args.need_semicolon(), mac, attrs)) - } - _ => unreachable!(), - } - } - StmtKind::Semi(expr) if matches!(expr.kind, ast::ExprKind::MacCall(..)) => { - match expr.into_inner() { - ast::Expr { kind: ast::ExprKind::MacCall(mac), attrs, .. } => { - Ok((mac.args.need_semicolon(), mac, attrs.into())) - } - _ => unreachable!(), - } - } - StmtKind::Local(..) | StmtKind::Empty | StmtKind::Item(..) | StmtKind::Semi(..) => { - Err(stmt) - } - StmtKind::Expr(..) => unreachable!(), - } - } - - fn configure(&mut self, node: T) -> Option { - self.cfg.configure(node) - } - // Detect use of feature-gated or invalid attributes on macro invocations // since they will not be detected after macro expansion. - fn check_attributes(&self, attrs: &[ast::Attribute], call: &MacCall) { + fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { let features = self.cx.ecfg.features.unwrap(); let mut attrs = attrs.iter().peekable(); let mut span: Option = None; @@ -1120,510 +1677,231 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } } } -} -/// Wraps a call to `noop_visit_*` / `noop_flat_map_*` -/// for an AST node that supports attributes -/// (see the `Annotatable` enum) -/// This method assigns a `NodeId`, and sets that `NodeId` -/// as our current 'lint node id'. If a macro call is found -/// inside this AST node, we will use this AST node's `NodeId` -/// to emit lints associated with that macro (allowing -/// `#[allow]` / `#[deny]` to be applied close to -/// the macro invocation). -/// -/// Do *not* call this for a macro AST node -/// (e.g. `ExprKind::MacCall`) - we cannot emit lints -/// at these AST nodes, since they are removed and -/// replaced with the result of macro expansion. -/// -/// All other `NodeId`s are assigned by `visit_id`. -/// * `self` is the 'self' parameter for the current method, -/// * `id` is a mutable reference to the `NodeId` field -/// of the current AST node. -/// * `closure` is a closure that executes the -/// `noop_visit_*` / `noop_flat_map_*` method -/// for the current AST node. -macro_rules! assign_id { - ($self:ident, $id:expr, $closure:expr) => {{ - let old_id = $self.cx.current_expansion.lint_node_id; - if $self.monotonic { - debug_assert_eq!(*$id, ast::DUMMY_NODE_ID); - let new_id = $self.cx.resolver.next_node_id(); - *$id = new_id; - $self.cx.current_expansion.lint_node_id = new_id; + fn expand_cfg_true( + &mut self, + node: &mut impl AstLike, + attr: ast::Attribute, + pos: usize, + ) -> bool { + let res = self.cfg.cfg_true(&attr); + if res { + // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion, + // and some tools like rustdoc and clippy rely on that. Find a way to remove them + // while keeping the tools working. + self.cx.expanded_inert_attrs.mark(&attr); + node.visit_attrs(|attrs| attrs.insert(pos, attr)); } - let ret = ($closure)(); - $self.cx.current_expansion.lint_node_id = old_id; - ret - }}; -} - -impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { - fn visit_crate(&mut self, krate: &mut ast::Crate) { - visit_clobber(krate, |krate| { - let span = krate.span; - let mut krate = match self.configure(krate) { - Some(krate) => krate, - None => { - return ast::Crate { - attrs: Vec::new(), - items: Vec::new(), - span, - id: self.cx.resolver.next_node_id(), - is_placeholder: false, - }; - } - }; - - if let Some(attr) = self.take_first_attr(&mut krate) { - return self - .collect_attr(attr, Annotatable::Crate(krate), AstFragmentKind::Crate) - .make_crate(); - } - - assign_id!(self, &mut krate.id, || noop_visit_crate(&mut krate, self)); - krate - }) + res } - fn visit_expr(&mut self, expr: &mut P) { - self.cfg.configure_expr(expr); - visit_clobber(expr.deref_mut(), |mut expr| { - if let Some(attr) = self.take_first_attr(&mut expr) { - // Collect the invoc regardless of whether or not attributes are permitted here - // expansion will eat the attribute so it won't error later. - self.cfg.maybe_emit_expr_attr_err(&attr.0); - - // AstFragmentKind::Expr requires the macro to emit an expression. - return self - .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::Expr) - .make_expr() - .into_inner(); - } - - if let ast::ExprKind::MacCall(mac) = expr.kind { - self.check_attributes(&expr.attrs, &mac); - self.collect_bang(mac, AstFragmentKind::Expr).make_expr().into_inner() - } else { - assign_id!(self, &mut expr.id, || { - ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self)); - }); - expr - } + fn expand_cfg_attr(&self, node: &mut impl AstLike, attr: ast::Attribute, pos: usize) { + node.visit_attrs(|attrs| { + attrs.splice(pos..pos, self.cfg.expand_cfg_attr(attr, false)); }); } - fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> { - let mut arm = configure!(self, arm); - - if let Some(attr) = self.take_first_attr(&mut arm) { - return self - .collect_attr(attr, Annotatable::Arm(arm), AstFragmentKind::Arms) - .make_arms(); + fn flat_map_node>( + &mut self, + mut node: Node, + ) -> Node::OutputTy { + loop { + return match self.take_first_attr(&mut node) { + Some((attr, pos, derives)) => match attr.name_or_empty() { + sym::cfg => { + if self.expand_cfg_true(&mut node, attr, pos) { + continue; + } + Default::default() + } + sym::cfg_attr => { + self.expand_cfg_attr(&mut node, attr, pos); + continue; + } + _ => { + Node::pre_flat_map_node_collect_attr(&self.cfg, &attr); + self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND) + .make_ast::() + } + }, + None if node.is_mac_call() => { + let (mac, attrs, add_semicolon) = node.take_mac_call(); + self.check_attributes(&attrs, &mac); + let mut res = self.collect_bang(mac, Node::KIND).make_ast::(); + Node::post_flat_map_node_collect_bang(&mut res, add_semicolon); + res + } + None => { + match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| { + assign_id!(this, node.id(), || node.noop_flat_map(this)) + }) { + Ok(output) => output, + Err(returned_node) => { + node = returned_node; + continue; + } + } + } + }; } - - assign_id!(self, &mut arm.id, || noop_flat_map_arm(arm, self)) } - fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { - let mut field = configure!(self, field); - - if let Some(attr) = self.take_first_attr(&mut field) { - return self - .collect_attr(attr, Annotatable::ExprField(field), AstFragmentKind::Fields) - .make_expr_fields(); + fn visit_node + DummyAstNode>( + &mut self, + node: &mut Node, + ) { + loop { + return match self.take_first_attr(node) { + Some((attr, pos, derives)) => match attr.name_or_empty() { + sym::cfg => { + let span = attr.span; + if self.expand_cfg_true(node, attr, pos) { + continue; + } + let msg = + format!("removing {} is not supported in this position", Node::descr()); + self.cx.span_err(span, &msg); + continue; + } + sym::cfg_attr => { + self.expand_cfg_attr(node, attr, pos); + continue; + } + _ => visit_clobber(node, |node| { + self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND) + .make_ast::() + }), + }, + None if node.is_mac_call() => { + visit_clobber(node, |node| { + // Do not clobber unless it's actually a macro (uncommon case). + let (mac, attrs, _) = node.take_mac_call(); + self.check_attributes(&attrs, &mac); + self.collect_bang(mac, Node::KIND).make_ast::() + }) + } + None => { + assign_id!(self, node.id(), || node.noop_visit(self)) + } + }; } - - assign_id!(self, &mut field.id, || noop_flat_map_expr_field(field, self)) } +} - fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> { - let mut fp = configure!(self, fp); - - if let Some(attr) = self.take_first_attr(&mut fp) { - return self - .collect_attr(attr, Annotatable::PatField(fp), AstFragmentKind::FieldPats) - .make_pat_fields(); - } - - assign_id!(self, &mut fp.id, || noop_flat_map_pat_field(fp, self)) +impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { + fn flat_map_item(&mut self, node: P) -> SmallVec<[P; 1]> { + self.flat_map_node(node) } - fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> { - let mut p = configure!(self, p); - - if let Some(attr) = self.take_first_attr(&mut p) { - return self - .collect_attr(attr, Annotatable::Param(p), AstFragmentKind::Params) - .make_params(); - } - - assign_id!(self, &mut p.id, || noop_flat_map_param(p, self)) + fn flat_map_trait_item(&mut self, node: P) -> SmallVec<[P; 1]> { + self.flat_map_node(AstLikeWrapper::new(node, TraitItemTag)) } - fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { - let mut sf = configure!(self, sf); - - if let Some(attr) = self.take_first_attr(&mut sf) { - return self - .collect_attr(attr, Annotatable::FieldDef(sf), AstFragmentKind::StructFields) - .make_field_defs(); - } - - assign_id!(self, &mut sf.id, || noop_flat_map_field_def(sf, self)) + fn flat_map_impl_item(&mut self, node: P) -> SmallVec<[P; 1]> { + self.flat_map_node(AstLikeWrapper::new(node, ImplItemTag)) } - fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> { - let mut variant = configure!(self, variant); - - if let Some(attr) = self.take_first_attr(&mut variant) { - return self - .collect_attr(attr, Annotatable::Variant(variant), AstFragmentKind::Variants) - .make_variants(); - } + fn flat_map_foreign_item( + &mut self, + node: P, + ) -> SmallVec<[P; 1]> { + self.flat_map_node(node) + } - assign_id!(self, &mut variant.id, || noop_flat_map_variant(variant, self)) + fn flat_map_variant(&mut self, node: ast::Variant) -> SmallVec<[ast::Variant; 1]> { + self.flat_map_node(node) } - fn filter_map_expr(&mut self, expr: P) -> Option> { - let expr = configure!(self, expr); - expr.filter_map(|mut expr| { - if let Some(attr) = self.take_first_attr(&mut expr) { - self.cfg.maybe_emit_expr_attr_err(&attr.0); + fn flat_map_field_def(&mut self, node: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { + self.flat_map_node(node) + } - return self - .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::OptExpr) - .make_opt_expr() - .map(|expr| expr.into_inner()); - } + fn flat_map_pat_field(&mut self, node: ast::PatField) -> SmallVec<[ast::PatField; 1]> { + self.flat_map_node(node) + } - if let ast::ExprKind::MacCall(mac) = expr.kind { - self.check_attributes(&expr.attrs, &mac); - self.collect_bang(mac, AstFragmentKind::OptExpr) - .make_opt_expr() - .map(|expr| expr.into_inner()) - } else { - assign_id!(self, &mut expr.id, || { - Some({ - noop_visit_expr(&mut expr, self); - expr - }) - }) - } - }) + fn flat_map_expr_field(&mut self, node: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { + self.flat_map_node(node) } - fn visit_pat(&mut self, pat: &mut P) { - match pat.kind { - PatKind::MacCall(_) => {} - _ => return noop_visit_pat(pat, self), - } + fn flat_map_param(&mut self, node: ast::Param) -> SmallVec<[ast::Param; 1]> { + self.flat_map_node(node) + } - visit_clobber(pat, |mut pat| match mem::replace(&mut pat.kind, PatKind::Wild) { - PatKind::MacCall(mac) => self.collect_bang(mac, AstFragmentKind::Pat).make_pat(), - _ => unreachable!(), - }); + fn flat_map_generic_param( + &mut self, + node: ast::GenericParam, + ) -> SmallVec<[ast::GenericParam; 1]> { + self.flat_map_node(node) } - fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { - let mut stmt = configure!(self, stmt); + fn flat_map_arm(&mut self, node: ast::Arm) -> SmallVec<[ast::Arm; 1]> { + self.flat_map_node(node) + } - // We pull macro invocations (both attributes and fn-like macro calls) out of their - // `StmtKind`s and treat them as statement macro invocations, not as items or expressions. + fn flat_map_stmt(&mut self, mut node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { // FIXME: invocations in semicolon-less expressions positions are expanded as expressions, // changing that requires some compatibility measures. - let mut stmt = if !stmt.is_expr() { - if let Some(attr) = self.take_first_attr(&mut stmt) { - return self - .collect_attr(attr, Annotatable::Stmt(P(stmt)), AstFragmentKind::Stmts) - .make_stmts(); - } - - match self.take_stmt_bang(stmt) { - Ok((add_semicolon, mac, attrs)) => { - self.check_attributes(&attrs, &mac); - let mut stmts = self.collect_bang(mac, AstFragmentKind::Stmts).make_stmts(); - - // If this is a macro invocation with a semicolon, then apply that - // semicolon to the final statement produced by expansion. - if add_semicolon { - if let Some(stmt) = stmts.pop() { - stmts.push(stmt.add_trailing_semicolon()); - } - } - - return stmts; + if node.is_expr() { + // The only way that we can end up with a `MacCall` expression statement, + // (as opposed to a `StmtKind::MacCall`) is if we have a macro as the + // traiing expression in a block (e.g. `fn foo() { my_macro!() }`). + // Record this information, so that we can report a more specific + // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed. + // See #78991 for an investigation of treating macros in this position + // as statements, rather than expressions, during parsing. + return match &node.kind { + StmtKind::Expr(expr) + if matches!(**expr, ast::Expr { kind: ExprKind::MacCall(..), .. }) => + { + self.cx.current_expansion.is_trailing_mac = true; + // Don't use `assign_id` for this statement - it may get removed + // entirely due to a `#[cfg]` on the contained expression + let res = noop_flat_map_stmt(node, self); + self.cx.current_expansion.is_trailing_mac = false; + res } - Err(stmt) => stmt, - } - } else { - stmt - }; - - // The only way that we can end up with a `MacCall` expression statement, - // (as opposed to a `StmtKind::MacCall`) is if we have a macro as the - // traiing expression in a block (e.g. `fn foo() { my_macro!() }`). - // Record this information, so that we can report a more specific - // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed. - // See #78991 for an investigation of treating macros in this position - // as statements, rather than expressions, during parsing. - let res = match &stmt.kind { - StmtKind::Expr(expr) - if matches!(**expr, ast::Expr { kind: ast::ExprKind::MacCall(..), .. }) => - { - self.cx.current_expansion.is_trailing_mac = true; - // Don't use `assign_id` for this statement - it may get removed - // entirely due to a `#[cfg]` on the contained expression - noop_flat_map_stmt(stmt, self) - } - _ => assign_id!(self, &mut stmt.id, || noop_flat_map_stmt(stmt, self)), - }; - self.cx.current_expansion.is_trailing_mac = false; - res - } - - fn visit_block(&mut self, block: &mut P) { - let orig_dir_ownership = mem::replace( - &mut self.cx.current_expansion.dir_ownership, - DirOwnership::UnownedViaBlock, - ); - noop_visit_block(block, self); - self.cx.current_expansion.dir_ownership = orig_dir_ownership; - } - - fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { - let mut item = configure!(self, item); - - if let Some(attr) = self.take_first_attr(&mut item) { - return self - .collect_attr(attr, Annotatable::Item(item), AstFragmentKind::Items) - .make_items(); + _ => assign_id!(self, &mut node.id, || noop_flat_map_stmt(node, self)), + }; } - let mut attrs = mem::take(&mut item.attrs); // We do this to please borrowck. - let ident = item.ident; - let span = item.span; - - match item.kind { - ast::ItemKind::MacCall(ref mac) => { - self.check_attributes(&attrs, &mac); - item.attrs = attrs; - item.and_then(|item| match item.kind { - ItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::Items).make_items() - } - _ => unreachable!(), - }) - } - ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::empty() => { - let (file_path, dir_path, dir_ownership) = match mod_kind { - ModKind::Loaded(_, inline, _) => { - // Inline `mod foo { ... }`, but we still need to push directories. - let (dir_path, dir_ownership) = mod_dir_path( - &self.cx.sess, - ident, - &attrs, - &self.cx.current_expansion.module, - self.cx.current_expansion.dir_ownership, - *inline, - ); - item.attrs = attrs; - (None, dir_path, dir_ownership) - } - ModKind::Unloaded => { - // We have an outline `mod foo;` so we need to parse the file. - let old_attrs_len = attrs.len(); - let ParsedExternalMod { - mut items, - inner_span, - file_path, - dir_path, - dir_ownership, - } = parse_external_mod( - &self.cx.sess, - ident, - span, - &self.cx.current_expansion.module, - self.cx.current_expansion.dir_ownership, - &mut attrs, - ); - - if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded { - (attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span); - } - - *mod_kind = ModKind::Loaded(items, Inline::No, inner_span); - item.attrs = attrs; - if item.attrs.len() > old_attrs_len { - // If we loaded an out-of-line module and added some inner attributes, - // then we need to re-configure it and re-collect attributes for - // resolution and expansion. - item = configure!(self, item); - - if let Some(attr) = self.take_first_attr(&mut item) { - return self - .collect_attr( - attr, - Annotatable::Item(item), - AstFragmentKind::Items, - ) - .make_items(); - } - } - (Some(file_path), dir_path, dir_ownership) - } - }; - - // Set the module info before we flat map. - let mut module = self.cx.current_expansion.module.with_dir_path(dir_path); - module.mod_path.push(ident); - if let Some(file_path) = file_path { - module.file_path_stack.push(file_path); - } - - let orig_module = - mem::replace(&mut self.cx.current_expansion.module, Rc::new(module)); - let orig_dir_ownership = - mem::replace(&mut self.cx.current_expansion.dir_ownership, dir_ownership); - - let result = assign_id!(self, &mut item.id, || noop_flat_map_item(item, self)); - - // Restore the module info. - self.cx.current_expansion.dir_ownership = orig_dir_ownership; - self.cx.current_expansion.module = orig_module; - - result - } - _ => { - item.attrs = attrs; - // The crate root is special - don't assign an ID to it. - if !(matches!(item.kind, ast::ItemKind::Mod(..)) && ident == Ident::empty()) { - assign_id!(self, &mut item.id, || noop_flat_map_item(item, self)) - } else { - noop_flat_map_item(item, self) - } - } - } + self.flat_map_node(node) } - fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { - let mut item = configure!(self, item); - - if let Some(attr) = self.take_first_attr(&mut item) { - return self - .collect_attr(attr, Annotatable::TraitItem(item), AstFragmentKind::TraitItems) - .make_trait_items(); - } - - match item.kind { - ast::AssocItemKind::MacCall(ref mac) => { - self.check_attributes(&item.attrs, &mac); - item.and_then(|item| match item.kind { - ast::AssocItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::TraitItems).make_trait_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self)) - } - } + fn visit_crate(&mut self, node: &mut ast::Crate) { + self.visit_node(node) } - fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { - let mut item = configure!(self, item); - - if let Some(attr) = self.take_first_attr(&mut item) { - return self - .collect_attr(attr, Annotatable::ImplItem(item), AstFragmentKind::ImplItems) - .make_impl_items(); - } - - match item.kind { - ast::AssocItemKind::MacCall(ref mac) => { - self.check_attributes(&item.attrs, &mac); - item.and_then(|item| match item.kind { - ast::AssocItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::ImplItems).make_impl_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self)) - } - } + fn visit_ty(&mut self, node: &mut P) { + self.visit_node(node) } - fn visit_ty(&mut self, ty: &mut P) { - match ty.kind { - ast::TyKind::MacCall(_) => {} - _ => return noop_visit_ty(ty, self), - }; - - visit_clobber(ty, |mut ty| match mem::replace(&mut ty.kind, ast::TyKind::Err) { - ast::TyKind::MacCall(mac) => self.collect_bang(mac, AstFragmentKind::Ty).make_ty(), - _ => unreachable!(), - }); + fn visit_pat(&mut self, node: &mut P) { + self.visit_node(node) } - fn flat_map_foreign_item( - &mut self, - foreign_item: P, - ) -> SmallVec<[P; 1]> { - let mut foreign_item = configure!(self, foreign_item); - - if let Some(attr) = self.take_first_attr(&mut foreign_item) { - return self - .collect_attr( - attr, - Annotatable::ForeignItem(foreign_item), - AstFragmentKind::ForeignItems, - ) - .make_foreign_items(); - } - - match foreign_item.kind { - ast::ForeignItemKind::MacCall(ref mac) => { - self.check_attributes(&foreign_item.attrs, &mac); - foreign_item.and_then(|item| match item.kind { - ast::ForeignItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::ForeignItems).make_foreign_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut foreign_item.id, || noop_flat_map_foreign_item( - foreign_item, - self - )) - } + fn visit_expr(&mut self, node: &mut P) { + // FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`. + if let Some(attr) = node.attrs.first() { + self.cfg.maybe_emit_expr_attr_err(attr); } + self.visit_node(node) } - fn flat_map_generic_param( - &mut self, - param: ast::GenericParam, - ) -> SmallVec<[ast::GenericParam; 1]> { - let mut param = configure!(self, param); - - if let Some(attr) = self.take_first_attr(&mut param) { - return self - .collect_attr( - attr, - Annotatable::GenericParam(param), - AstFragmentKind::GenericParams, - ) - .make_generic_params(); - } + fn filter_map_expr(&mut self, node: P) -> Option> { + self.flat_map_node(AstLikeWrapper::new(node, OptExprTag)) + } - assign_id!(self, &mut param.id, || noop_flat_map_generic_param(param, self)) + fn visit_block(&mut self, node: &mut P) { + let orig_dir_ownership = mem::replace( + &mut self.cx.current_expansion.dir_ownership, + DirOwnership::UnownedViaBlock, + ); + noop_visit_block(node, self); + self.cx.current_expansion.dir_ownership = orig_dir_ownership; } - fn visit_id(&mut self, id: &mut ast::NodeId) { + fn visit_id(&mut self, id: &mut NodeId) { // We may have already assigned a `NodeId` // by calling `assign_id` if self.monotonic && *id == ast::DUMMY_NODE_ID { diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 47a64b457d..43a310f4ea 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,6 +1,7 @@ +#![feature(associated_type_bounds)] +#![feature(associated_type_defaults)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] -#![cfg_attr(bootstrap, feature(destructuring_assignment))] #![feature(if_let_guard)] #![feature(let_else)] #![feature(proc_macro_diagnostic)] @@ -8,6 +9,7 @@ #![feature(proc_macro_span)] #![feature(try_blocks)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 8065911afb..f71fb58cf6 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1039,7 +1039,7 @@ fn check_matcher_core( )); err.span_suggestion( span, - &format!("try a `pat_param` fragment specifier instead"), + "try a `pat_param` fragment specifier instead", suggestion, Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 825af9a7b2..af593e9263 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -123,7 +123,7 @@ pub fn placeholder( span, is_placeholder: true, }]), - AstFragmentKind::Fields => AstFragment::Fields(smallvec![ast::ExprField { + AstFragmentKind::ExprFields => AstFragment::ExprFields(smallvec![ast::ExprField { attrs: Default::default(), expr: expr_placeholder(), id, @@ -132,7 +132,7 @@ pub fn placeholder( span, is_placeholder: true, }]), - AstFragmentKind::FieldPats => AstFragment::FieldPats(smallvec![ast::PatField { + AstFragmentKind::PatFields => AstFragment::PatFields(smallvec![ast::PatField { attrs: Default::default(), id, ident, @@ -159,7 +159,7 @@ pub fn placeholder( ty: ty(), is_placeholder: true, }]), - AstFragmentKind::StructFields => AstFragment::StructFields(smallvec![ast::FieldDef { + AstFragmentKind::FieldDefs => AstFragment::FieldDefs(smallvec![ast::FieldDef { attrs: Default::default(), id, ident: None, diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 5656465655..efbe0b6571 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -158,7 +158,7 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec, &mut Rustc<'_, '_>)> for ch in data.as_str().chars() { escaped.extend(ch.escape_debug()); } - let stream = vec![ + let stream = [ Ident(sym::doc, false), Eq, TokenKind::lit(token::Str, Symbol::intern(&escaped), None), @@ -221,7 +221,7 @@ impl ToInternal for TokenTree { let integer = TokenKind::lit(token::Integer, symbol, suffix); let a = tokenstream::TokenTree::token(minus, span); let b = tokenstream::TokenTree::token(integer, span); - return vec![a, b].into_iter().collect(); + return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { lit: token::Lit { kind: token::Float, symbol, suffix }, @@ -232,7 +232,7 @@ impl ToInternal for TokenTree { let float = TokenKind::lit(token::Float, symbol, suffix); let a = tokenstream::TokenTree::token(minus, span); let b = tokenstream::TokenTree::token(float, span); - return vec![a, b].into_iter().collect(); + return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { lit, span }) => { return tokenstream::TokenTree::token(Literal(lit), span).into(); diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 32a9d081ed..d43f926d0a 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -70,6 +70,8 @@ declare_features! ( (accepted, cfg_attr_multi, "1.33.0", Some(54881), None), /// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests. (accepted, cfg_doctest, "1.40.0", Some(62210), None), + /// Enables `#[cfg(panic = "...")]` config key. + (accepted, cfg_panic, "1.60.0", Some(77443), None), /// Allows `cfg(target_feature = "...")`. (accepted, cfg_target_feature, "1.27.0", Some(29717), None), /// Allows `cfg(target_vendor = "...")`. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index ebd12d6ab4..5545abc602 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -161,6 +161,12 @@ declare_features! ( (active, staged_api, "1.0.0", None, None), /// Added for testing E0705; perma-unstable. (active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)), + /// Allows non-`unsafe` —and thus, unsound— access to `Pin` constructions. + /// Marked `incomplete` since perma-unstable and unsound. + (incomplete, unsafe_pin_internals, "1.60.0", None, None), + /// Use for stable + negative coherence and strict coherence depending on trait's + /// rustc_strict_coherence value. + (active, with_negative_coherence, "1.60.0", None, None), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! @@ -274,20 +280,20 @@ declare_features! ( (incomplete, adt_const_params, "1.56.0", Some(44580), None), /// Allows defining an `#[alloc_error_handler]`. (active, alloc_error_handler, "1.29.0", Some(51540), None), - /// Allows a test to fail without failing the whole suite. - (active, allow_fail, "1.19.0", Some(46488), None), /// Allows explicit discriminants on non-unit enum variants. (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None), /// Allows trait methods with arbitrary self types. (active, arbitrary_self_types, "1.23.0", Some(44874), None), /// Allows using `const` operands in inline assembly. - (active, asm_const, "1.58.0", Some(72016), None), + (active, asm_const, "1.58.0", Some(93332), None), /// Enables experimental inline assembly support for additional architectures. - (active, asm_experimental_arch, "1.58.0", Some(72016), None), + (active, asm_experimental_arch, "1.58.0", Some(93335), None), /// Allows using `sym` operands in inline assembly. - (active, asm_sym, "1.58.0", Some(72016), None), + (active, asm_sym, "1.58.0", Some(93333), None), /// Allows the `may_unwind` option in inline assembly. - (active, asm_unwind, "1.58.0", Some(72016), None), + (active, asm_unwind, "1.58.0", Some(93334), None), + /// Allows users to enforce equality of associated constants `TraitImpl`. + (active, associated_const_equality, "1.58.0", Some(92827), None), /// Allows the user of associated type bounds. (active, associated_type_bounds, "1.34.0", Some(52662), None), /// Allows associated type defaults. @@ -300,14 +306,14 @@ declare_features! ( (active, c_variadic, "1.34.0", Some(44930), None), /// Allows capturing disjoint fields in a closure/generator (RFC 2229). (incomplete, capture_disjoint_fields, "1.49.0", Some(53488), None), - /// Enables `#[cfg(panic = "...")]` config key. - (active, cfg_panic, "1.49.0", Some(77443), None), /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. (active, cfg_sanitize, "1.41.0", Some(39699), None), /// Allows `cfg(target_abi = "...")`. (active, cfg_target_abi, "1.55.0", Some(80970), None), - /// Allows `cfg(target_has_atomic = "...")`. - (active, cfg_target_has_atomic, "1.9.0", Some(32976), None), + /// Allows `cfg(target_has_atomic_load_store = "...")`. + (active, cfg_target_has_atomic, "1.60.0", Some(94039), None), + /// Allows `cfg(target_has_atomic_equal_alignment = "...")`. + (active, cfg_target_has_atomic_equal_alignment, "1.60.0", Some(93822), None), /// Allows `cfg(target_thread_local)`. (active, cfg_target_thread_local, "1.7.0", Some(29594), None), /// Allow conditional compilation depending on rust version @@ -327,7 +333,7 @@ declare_features! ( /// Allows using and casting function pointers in a `const fn`. (active, const_fn_fn_ptr_basics, "1.48.0", Some(57563), None), /// Allows trait bounds in `const fn`. - (active, const_fn_trait_bound, "1.53.0", Some(57563), None), + (active, const_fn_trait_bound, "1.53.0", Some(93706), None), /// Allows `for _ in _` loops in const contexts. (active, const_for, "1.56.0", Some(87575), None), /// Allows argument and return position `impl Trait` in a `const fn`. @@ -413,7 +419,7 @@ declare_features! ( // Allows setting the threshold for the `large_assignments` lint. (active, large_assignments, "1.52.0", Some(83518), None), /// Allows `if/while p && let q = r && ...` chains. - (incomplete, let_chains, "1.37.0", Some(53667), None), + (active, let_chains, "1.37.0", Some(53667), None), /// Allows `let...else` statements. (active, let_else, "1.56.0", Some(87335), None), /// Allows `#[link(..., cfg(..))]`. @@ -528,6 +534,8 @@ declare_features! ( /// /// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0. (active, untagged_unions, "1.13.0", Some(55149), None), + /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. + (active, used_with_arg, "1.60.0", Some(93798), None), /// Allows `extern "wasm" fn` (active, wasm_abi, "1.53.0", Some(83788), None), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 0644948704..1fb1a38a92 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -26,16 +26,14 @@ const GATED_CFGS: &[GatedCfg] = &[ // (name in cfg, feature, function to check if the feature is enabled) (sym::target_abi, sym::cfg_target_abi, cfg_fn!(cfg_target_abi)), (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)), - (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), - (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), ( sym::target_has_atomic_equal_alignment, - sym::cfg_target_has_atomic, - cfg_fn!(cfg_target_has_atomic), + sym::cfg_target_has_atomic_equal_alignment, + cfg_fn!(cfg_target_has_atomic_equal_alignment), ), + (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)), (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)), (sym::version, sym::cfg_version, cfg_fn!(cfg_version)), - (sym::panic, sym::cfg_panic, cfg_fn!(cfg_panic)), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. @@ -324,7 +322,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_mangle, Normal, template!(Word), WarnFollowing), - ungated!(used, Normal, template!(Word), WarnFollowing), + ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing), // Limits: ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), @@ -351,7 +349,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Runtime ungated!( - windows_subsystem, Normal, + windows_subsystem, CrateLevel, template!(NameValueStr: "windows|console"), FutureWarnFollowing ), ungated!(panic_handler, Normal, template!(Word), WarnFollowing), // RFC 2070 @@ -359,7 +357,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Code generation: ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing), ungated!(cold, Normal, template!(Word), WarnFollowing), - ungated!(no_builtins, Normal, template!(Word), WarnFollowing), + ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing), ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk), ungated!(track_caller, Normal, template!(Word), WarnFollowing), gated!( @@ -402,7 +400,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ }, // Testing: - gated!(allow_fail, Normal, template!(Word), WarnFollowing, experimental!(allow_fail)), gated!( test_runner, CrateLevel, template!(List: "path"), ErrorFollowing, custom_test_frameworks, "custom test frameworks are an unstable feature", @@ -580,6 +577,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_trivial_field_reads, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE ), + // Used by the `rustc::potential_query_instability` lint to warn methods which + // might not be stable during incremental compilation. + rustc_attr!(rustc_lint_query_instability, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Const related: @@ -622,6 +622,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, lang_items, "language items are subject to change", ), + rustc_attr!( + rustc_pass_by_value, Normal, + template!(Word), ErrorFollowing, + "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." + ), BuiltinAttribute { name: sym::rustc_diagnostic_item, type_: Normal, @@ -676,6 +681,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \ from method dispatch when the receiver is an array, for compatibility in editions < 2021." ), + rustc_attr!( + rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."), ErrorFollowing, + "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \ + definition of a trait, it's currently in experimental form and should be changed before \ + being exposed outside of the std" + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index b9f3b5ad1b..f5f944db5e 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -48,6 +48,8 @@ declare_features! ( (removed, advanced_slice_patterns, "1.0.0", Some(62254), None, Some("merged into `#![feature(slice_patterns)]`")), (removed, allocator, "1.0.0", None, None, None), + /// Allows a test to fail without failing the whole suite. + (removed, allow_fail, "1.19.0", Some(46488), None, Some("removed due to no clear use cases")), (removed, await_macro, "1.38.0", Some(50547), None, Some("subsumed by `.await` syntax")), /// Allows comparing raw pointers during const eval. diff --git a/compiler/rustc_graphviz/src/tests.rs b/compiler/rustc_graphviz/src/tests.rs index a297bac86c..154bae4cb0 100644 --- a/compiler/rustc_graphviz/src/tests.rs +++ b/compiler/rustc_graphviz/src/tests.rs @@ -56,7 +56,7 @@ impl NodeLabels<&'static str> { match self { UnlabelledNodes(len) => vec![None; len], AllNodesLabelled(lbls) => lbls.into_iter().map(Some).collect(), - SomeNodesLabelled(lbls) => lbls.into_iter().collect(), + SomeNodesLabelled(lbls) => lbls, } } diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index edad00ed6a..27ec461906 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -30,7 +30,6 @@ macro_rules! arena_types { [] impl_item_ref: rustc_hir::ImplItemRef, [] item: rustc_hir::Item<'tcx>, [] inline_asm: rustc_hir::InlineAsm<'tcx>, - [] llvm_inline_asm: rustc_hir::LlvmInlineAsm<'tcx>, [] local: rustc_hir::Local<'tcx>, [] mod_: rustc_hir::Mod<'tcx>, [] owner_info: rustc_hir::OwnerInfo<'tcx>, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index a43cb0203d..e99f61d034 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -266,59 +266,67 @@ pub enum Res { /// /// **Belongs to the type namespace.** PrimTy(hir::PrimTy), - /// The `Self` type, optionally with the trait it is associated with - /// and optionally with the [`DefId`] of the impl it is associated with. + /// The `Self` type, optionally with the [`DefId`] of the trait it belongs to and + /// optionally with the [`DefId`] of the item introducing the `Self` type alias. /// /// **Belongs to the type namespace.** /// - /// For example, the `Self` in - /// + /// Examples: /// ``` + /// struct Bar(Box); + /// // `Res::SelfTy { trait_: None, alias_of: Some(Bar) }` + /// /// trait Foo { /// fn foo() -> Box; + /// // `Res::SelfTy { trait_: Some(Foo), alias_of: None }` /// } - /// ``` - /// - /// would have the [`DefId`] of `Foo` associated with it. The `Self` in - /// - /// ``` - /// struct Bar; /// /// impl Bar { - /// fn new() -> Self { Bar } + /// fn blah() { + /// let _: Self; + /// // `Res::SelfTy { trait_: None, alias_of: Some(::{impl#0}) }` + /// } /// } - /// ``` - /// - /// would have the [`DefId`] of the impl associated with it. Finally, the `Self` in /// - /// ``` /// impl Foo for Bar { - /// fn foo() -> Box { Box::new(Bar) } + /// fn foo() -> Box { + /// // `Res::SelfTy { trait_: Some(Foo), alias_of: Some(::{impl#1}) }` + /// let _: Self; + /// // `Res::SelfTy { trait_: Some(Foo), alias_of: Some(::{impl#1}) }` + /// + /// todo!() + /// } /// } /// ``` /// - /// would have both the [`DefId`] of `Foo` and the [`DefId`] of the impl - /// associated with it. - /// /// *See also [`Res::SelfCtor`].* /// /// ----- /// - /// HACK(min_const_generics): impl self types also have an optional requirement to **not** mention + /// HACK(min_const_generics): self types also have an optional requirement to **not** mention /// any generic parameters to allow the following with `min_const_generics`: /// ``` /// impl Foo { fn test() -> [u8; std::mem::size_of::()] { todo!() } } + /// + /// struct Bar([u8; baz::()]); + /// const fn baz() -> usize { 10 } /// ``` /// We do however allow `Self` in repeat expression even if it is generic to not break code - /// which already works on stable while causing the `const_evaluatable_unchecked` future compat lint. - /// - /// FIXME(generic_const_exprs): Remove this bodge once that feature is stable. - SelfTy( - /// Optionally, the trait associated with this `Self` type. - Option, - /// Optionally, the impl associated with this `Self` type. - Option<(DefId, bool)>, - ), + /// which already works on stable while causing the `const_evaluatable_unchecked` future compat lint: + /// ``` + /// fn foo() { + /// let _bar = [1_u8; std::mem::size_of::<*mut T>()]; + /// } + /// ``` + // FIXME(generic_const_exprs): Remove this bodge once that feature is stable. + SelfTy { + /// The trait this `Self` is a generic arg for. + trait_: Option, + /// The item introducing the `Self` type alias. Can be used in the `type_of` query + /// to get the underlying type. Additionally whether the `Self` type is disallowed + /// from mentioning generics (i.e. when used in an anonymous constant). + alias_to: Option<(DefId, bool)>, + }, /// A tool attribute module; e.g., the `rustfmt` in `#[rustfmt::skip]`. /// /// **Belongs to the type namespace.** @@ -550,7 +558,7 @@ impl Res { Res::Local(..) | Res::PrimTy(..) - | Res::SelfTy(..) + | Res::SelfTy { .. } | Res::SelfCtor(..) | Res::ToolMod | Res::NonMacroAttr(..) @@ -573,7 +581,7 @@ impl Res { Res::SelfCtor(..) => "self constructor", Res::PrimTy(..) => "builtin type", Res::Local(..) => "local variable", - Res::SelfTy(..) => "self type", + Res::SelfTy { .. } => "self type", Res::ToolMod => "tool module", Res::NonMacroAttr(attr_kind) => attr_kind.descr(), Res::Err => "unresolved item", @@ -596,7 +604,7 @@ impl Res { Res::SelfCtor(id) => Res::SelfCtor(id), Res::PrimTy(id) => Res::PrimTy(id), Res::Local(id) => Res::Local(map(id)), - Res::SelfTy(a, b) => Res::SelfTy(a, b), + Res::SelfTy { trait_, alias_to } => Res::SelfTy { trait_, alias_to }, Res::ToolMod => Res::ToolMod, Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind), Res::Err => Res::Err, @@ -620,7 +628,7 @@ impl Res { pub fn ns(&self) -> Option { match self { Res::Def(kind, ..) => kind.ns(), - Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => Some(Namespace::TypeNS), + Res::PrimTy(..) | Res::SelfTy { .. } | Res::ToolMod => Some(Namespace::TypeNS), Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS), Res::NonMacroAttr(..) => Some(Namespace::MacroNS), Res::Err => None, diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index d813c887ee..d655f12f5e 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -7,7 +7,6 @@ pub use crate::def_id::DefPathHash; use crate::def_id::{CrateNum, DefIndex, LocalDefId, StableCrateId, CRATE_DEF_INDEX, LOCAL_CRATE}; use crate::def_path_hash_map::DefPathHashMap; -use crate::hir; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::StableHasher; @@ -101,15 +100,6 @@ impl DefPathTable { pub struct Definitions { table: DefPathTable, - /// Only [`LocalDefId`]s for items and item-like are HIR owners. - /// The associated `HirId` has a `local_id` of `0`. - /// Generic parameters and closures are also assigned a `LocalDefId` but are not HIR owners. - /// Their `HirId`s are defined by their position while lowering the enclosing owner. - // FIXME(cjgillot) Some `LocalDefId`s from `use` items are dropped during lowering and lack a `HirId`. - pub(super) def_id_to_hir_id: IndexVec>, - /// The reverse mapping of `def_id_to_hir_id`. - pub(super) hir_id_to_def_id: FxHashMap, - /// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`. expansions_that_defined: FxHashMap, @@ -324,17 +314,6 @@ impl Definitions { }) } - #[inline] - #[track_caller] - pub fn local_def_id_to_hir_id(&self, id: LocalDefId) -> hir::HirId { - self.def_id_to_hir_id[id].unwrap() - } - - #[inline] - pub fn opt_hir_id_to_local_def_id(&self, hir_id: hir::HirId) -> Option { - self.hir_id_to_def_id.get(&hir_id).copied() - } - /// Adds a root definition (no parent) and a few other reserved definitions. pub fn new(stable_crate_id: StableCrateId, crate_span: Span) -> Definitions { let key = DefKey { @@ -361,8 +340,6 @@ impl Definitions { Definitions { table, - def_id_to_hir_id: Default::default(), - hir_id_to_def_id: Default::default(), expansions_that_defined: Default::default(), def_id_to_span, stable_crate_id, @@ -414,26 +391,6 @@ impl Definitions { def_id } - /// Initializes the `LocalDefId` to `HirId` mapping once it has been generated during - /// AST to HIR lowering. - pub fn init_def_id_to_hir_id_mapping( - &mut self, - mapping: IndexVec>, - ) { - assert!( - self.def_id_to_hir_id.is_empty(), - "trying to initialize `LocalDefId` <-> `HirId` mappings twice" - ); - - // Build the reverse mapping of `def_id_to_hir_id`. - self.hir_id_to_def_id = mapping - .iter_enumerated() - .filter_map(|(def_id, hir_id)| hir_id.map(|hir_id| (hir_id, def_id))) - .collect(); - - self.def_id_to_hir_id = mapping; - } - pub fn expansion_that_defined(&self, id: LocalDefId) -> ExpnId { self.expansions_that_defined.get(&id).copied().unwrap_or_else(ExpnId::root) } @@ -445,17 +402,21 @@ impl Definitions { } pub fn iter_local_def_id(&self) -> impl Iterator + '_ { - self.def_id_to_hir_id.iter_enumerated().map(|(k, _)| k) + self.table.def_path_hashes.indices().map(|local_def_index| LocalDefId { local_def_index }) } #[inline(always)] - pub fn local_def_path_hash_to_def_id(&self, hash: DefPathHash) -> LocalDefId { + pub fn local_def_path_hash_to_def_id( + &self, + hash: DefPathHash, + err: &mut dyn FnMut() -> !, + ) -> LocalDefId { debug_assert!(hash.stable_crate_id() == self.stable_crate_id); self.table .def_path_hash_to_index .get(&hash) .map(|local_def_index| LocalDefId { local_def_index }) - .unwrap() + .unwrap_or_else(|| err()) } pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index d59756239d..0961d0131d 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -5,8 +5,8 @@ use crate::intravisit::FnKind; use crate::LangItem; use rustc_ast::util::parser::ExprPrecedence; -use rustc_ast::{self as ast, CrateSugar, LlvmAsmDialect}; -use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, TraitObjectSyntax, UintTy}; +use rustc_ast::{self as ast, CrateSugar}; +use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, TraitObjectSyntax, UintTy}; pub use rustc_ast::{BorrowKind, ImplPolarity, IsAuto}; pub use rustc_ast::{CaptureBy, Movability, Mutability}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -205,7 +205,6 @@ impl Path<'_> { #[derive(Debug, HashStable_Generic)] pub struct PathSegment<'hir> { /// The identifier portion of this path segment. - #[stable_hasher(project(name))] pub ident: Ident, // `id` and `res` are optional. We currently only use these in save-analysis, // any path segments without these will not have save-analysis info and @@ -294,10 +293,6 @@ impl GenericArg<'_> { } } - pub fn is_const(&self) -> bool { - matches!(self, GenericArg::Const(_)) - } - pub fn is_synthetic(&self) -> bool { matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::empty()) } @@ -319,6 +314,13 @@ impl GenericArg<'_> { GenericArg::Infer(_) => ast::ParamKindOrd::Infer, } } + + pub fn is_ty_or_const(&self) -> bool { + match self { + GenericArg::Lifetime(_) => false, + GenericArg::Type(_) | GenericArg::Const(_) | GenericArg::Infer(_) => true, + } + } } #[derive(Debug, HashStable_Generic)] @@ -375,7 +377,7 @@ impl GenericArgs<'_> { GenericArg::Type(ty) => matches!(ty.kind, TyKind::Err), _ => false, }) || self.bindings.iter().any(|arg| match arg.kind { - TypeBindingKind::Equality { ty } => matches!(ty.kind, TyKind::Err), + TypeBindingKind::Equality { term: Term::Ty(ty) } => matches!(ty.kind, TyKind::Err), _ => false, }) } @@ -638,9 +640,8 @@ impl<'hir> WhereBoundPredicate<'hir> { _ => return false, }; match path.res { - Res::Def(DefKind::TyParam, def_id) | Res::SelfTy(Some(def_id), None) => { - def_id == param_def_id - } + Res::Def(DefKind::TyParam, def_id) + | Res::SelfTy { trait_: Some(def_id), alias_to: None } => def_id == param_def_id, _ => false, } } @@ -705,6 +706,17 @@ pub struct OwnerNodes<'tcx> { pub nodes: IndexVec>>, /// Content of local bodies. pub bodies: SortedMap>, + /// Non-owning definitions contained in this owner. + pub local_id_to_def_id: SortedMap, +} + +impl<'tcx> OwnerNodes<'tcx> { + pub fn node(&self) -> OwnerNode<'tcx> { + use rustc_index::vec::Idx; + let node = self.nodes[ItemLocalId::new(0)].as_ref().unwrap().node; + let node = node.as_owner().unwrap(); // Indexing must ensure it is an OwnerNode. + node + } } /// Full information resulting from lowering an AST node. @@ -724,10 +736,39 @@ pub struct OwnerInfo<'hir> { impl<'tcx> OwnerInfo<'tcx> { #[inline] pub fn node(&self) -> OwnerNode<'tcx> { - use rustc_index::vec::Idx; - let node = self.nodes.nodes[ItemLocalId::new(0)].as_ref().unwrap().node; - let node = node.as_owner().unwrap(); // Indexing must ensure it is an OwnerNode. - node + self.nodes.node() + } +} + +#[derive(Copy, Clone, Debug, HashStable_Generic)] +pub enum MaybeOwner { + Owner(T), + NonOwner(HirId), + /// Used as a placeholder for unused LocalDefId. + Phantom, +} + +impl MaybeOwner { + pub fn as_owner(self) -> Option { + match self { + MaybeOwner::Owner(i) => Some(i), + MaybeOwner::NonOwner(_) | MaybeOwner::Phantom => None, + } + } + + pub fn map(self, f: impl FnOnce(T) -> U) -> MaybeOwner { + match self { + MaybeOwner::Owner(i) => MaybeOwner::Owner(f(i)), + MaybeOwner::NonOwner(hir_id) => MaybeOwner::NonOwner(hir_id), + MaybeOwner::Phantom => MaybeOwner::Phantom, + } + } + + pub fn unwrap(self) -> T { + match self { + MaybeOwner::Owner(i) => i, + MaybeOwner::NonOwner(_) | MaybeOwner::Phantom => panic!("Not a HIR owner"), + } } } @@ -739,7 +780,7 @@ impl<'tcx> OwnerInfo<'tcx> { /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html #[derive(Debug)] pub struct Crate<'hir> { - pub owners: IndexVec>>, + pub owners: IndexVec>>, pub hir_hash: Fingerprint, } @@ -850,7 +891,6 @@ pub struct PatField<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, /// The identifier for the field. - #[stable_hasher(project(name))] pub ident: Ident, /// The pattern the field is destructured to. pub pat: &'hir Pat<'hir>, @@ -1473,7 +1513,6 @@ impl Expr<'_> { ExprKind::Continue(..) => ExprPrecedence::Continue, ExprKind::Ret(..) => ExprPrecedence::Ret, ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm, - ExprKind::LlvmInlineAsm(..) => ExprPrecedence::InlineAsm, ExprKind::Struct(..) => ExprPrecedence::Struct, ExprKind::Repeat(..) => ExprPrecedence::Repeat, ExprKind::Yield(..) => ExprPrecedence::Yield, @@ -1533,7 +1572,6 @@ impl Expr<'_> { | ExprKind::Loop(..) | ExprKind::Assign(..) | ExprKind::InlineAsm(..) - | ExprKind::LlvmInlineAsm(..) | ExprKind::AssignOp(..) | ExprKind::Lit(_) | ExprKind::ConstBlock(..) @@ -1616,7 +1654,6 @@ impl Expr<'_> { | ExprKind::Loop(..) | ExprKind::Assign(..) | ExprKind::InlineAsm(..) - | ExprKind::LlvmInlineAsm(..) | ExprKind::AssignOp(..) | ExprKind::ConstBlock(..) | ExprKind::Box(..) @@ -1671,13 +1708,13 @@ pub enum ExprKind<'hir> { Call(&'hir Expr<'hir>, &'hir [Expr<'hir>]), /// A method call (e.g., `x.foo::<'static, Bar, Baz>(a, b, c, d)`). /// - /// The `PathSegment`/`Span` represent the method name and its generic arguments + /// The `PathSegment` represents the method name and its generic arguments /// (within the angle brackets). - /// The first element of the vector of `Expr`s is the expression that evaluates + /// The first element of the `&[Expr]` is the expression that evaluates /// to the object on which the method is being called on (the receiver), /// and the remaining elements are the rest of the arguments. /// Thus, `x.foo::(a, b, c, d)` is represented as - /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`. + /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d], span)`. /// The final `Span` represents the span of the function and arguments /// (e.g. `foo::(a, b, c, d)` in `x.foo::(a, b, c, d)` /// @@ -1685,7 +1722,7 @@ pub enum ExprKind<'hir> { /// the `hir_id` of the `MethodCall` node itself. /// /// [`type_dependent_def_id`]: ../ty/struct.TypeckResults.html#method.type_dependent_def_id - MethodCall(&'hir PathSegment<'hir>, Span, &'hir [Expr<'hir>], Span), + MethodCall(&'hir PathSegment<'hir>, &'hir [Expr<'hir>], Span), /// A tuple (e.g., `(a, b, c, d)`). Tup(&'hir [Expr<'hir>]), /// A binary operation (e.g., `a + b`, `a * b`). @@ -1757,8 +1794,6 @@ pub enum ExprKind<'hir> { /// Inline assembly (from `asm!`), with its outputs and inputs. InlineAsm(&'hir InlineAsm<'hir>), - /// Inline assembly (from `llvm_asm!`), with its outputs and inputs. - LlvmInlineAsm(&'hir LlvmInlineAsm<'hir>), /// A struct or struct-like variant literal expression. /// @@ -2076,7 +2111,6 @@ pub struct ImplItem<'hir> { pub ident: Ident, pub def_id: LocalDefId, pub vis: Visibility<'hir>, - pub defaultness: Defaultness, pub generics: Generics<'hir>, pub kind: ImplItemKind<'hir>, pub span: Span, @@ -2127,29 +2161,52 @@ pub const FN_OUTPUT_NAME: Symbol = sym::Output; #[derive(Debug, HashStable_Generic)] pub struct TypeBinding<'hir> { pub hir_id: HirId, - #[stable_hasher(project(name))] pub ident: Ident, pub gen_args: &'hir GenericArgs<'hir>, pub kind: TypeBindingKind<'hir>, pub span: Span, } +#[derive(Debug, HashStable_Generic)] +pub enum Term<'hir> { + Ty(&'hir Ty<'hir>), + Const(AnonConst), +} + +impl<'hir> From<&'hir Ty<'hir>> for Term<'hir> { + fn from(ty: &'hir Ty<'hir>) -> Self { + Term::Ty(ty) + } +} + +impl<'hir> From for Term<'hir> { + fn from(c: AnonConst) -> Self { + Term::Const(c) + } +} + // Represents the two kinds of type bindings. #[derive(Debug, HashStable_Generic)] pub enum TypeBindingKind<'hir> { /// E.g., `Foo`. Constraint { bounds: &'hir [GenericBound<'hir>] }, - /// E.g., `Foo`. - Equality { ty: &'hir Ty<'hir> }, + /// E.g., `Foo`, `Foo` + Equality { term: Term<'hir> }, } impl TypeBinding<'_> { pub fn ty(&self) -> &Ty<'_> { match self.kind { - TypeBindingKind::Equality { ref ty } => ty, + TypeBindingKind::Equality { term: Term::Ty(ref ty) } => ty, _ => panic!("expected equality type binding for parenthesized generic args"), } } + pub fn opt_const(&self) -> Option<&'_ AnonConst> { + match self.kind { + TypeBindingKind::Equality { term: Term::Const(ref c) } => Some(c), + _ => None, + } + } } #[derive(Debug)] @@ -2371,36 +2428,6 @@ pub struct InlineAsm<'hir> { pub line_spans: &'hir [Span], } -#[derive(Copy, Clone, Encodable, Decodable, Debug, Hash, HashStable_Generic, PartialEq)] -pub struct LlvmInlineAsmOutput { - pub constraint: Symbol, - pub is_rw: bool, - pub is_indirect: bool, - pub span: Span, -} - -// NOTE(eddyb) This is used within MIR as well, so unlike the rest of the HIR, -// it needs to be `Clone` and `Decodable` and use plain `Vec` instead of -// arena-allocated slice. -#[derive(Clone, Encodable, Decodable, Debug, Hash, HashStable_Generic, PartialEq)] -pub struct LlvmInlineAsmInner { - pub asm: Symbol, - pub asm_str_style: StrStyle, - pub outputs: Vec, - pub inputs: Vec, - pub clobbers: Vec, - pub volatile: bool, - pub alignstack: bool, - pub dialect: LlvmAsmDialect, -} - -#[derive(Debug, HashStable_Generic)] -pub struct LlvmInlineAsm<'hir> { - pub inner: LlvmInlineAsmInner, - pub outputs_exprs: &'hir [Expr<'hir>], - pub inputs_exprs: &'hir [Expr<'hir>], -} - /// Represents a parameter in a function header. #[derive(Debug, HashStable_Generic)] pub struct Param<'hir> { @@ -2515,7 +2542,6 @@ pub struct EnumDef<'hir> { #[derive(Debug, HashStable_Generic)] pub struct Variant<'hir> { /// Name of the variant. - #[stable_hasher(project(name))] pub ident: Ident, /// Id of the variant (not the constructor, see `VariantData::ctor_hir_id()`). pub id: HirId, @@ -2605,7 +2631,6 @@ impl VisibilityKind<'_> { #[derive(Debug, HashStable_Generic)] pub struct FieldDef<'hir> { pub span: Span, - #[stable_hasher(project(name))] pub ident: Ident, pub vis: Visibility<'hir>, pub hir_id: HirId, @@ -2745,6 +2770,10 @@ pub struct FnHeader { } impl FnHeader { + pub fn is_async(&self) -> bool { + matches!(&self.asyncness, IsAsync::Async) + } + pub fn is_const(&self) -> bool { matches!(&self.constness, Constness::Const) } @@ -2864,7 +2893,6 @@ impl ItemKind<'_> { #[derive(Encodable, Debug, HashStable_Generic)] pub struct TraitItemRef { pub id: TraitItemId, - #[stable_hasher(project(name))] pub ident: Ident, pub kind: AssocItemKind, pub span: Span, @@ -2880,11 +2908,12 @@ pub struct TraitItemRef { #[derive(Debug, HashStable_Generic)] pub struct ImplItemRef { pub id: ImplItemId, - #[stable_hasher(project(name))] pub ident: Ident, pub kind: AssocItemKind, pub span: Span, pub defaultness: Defaultness, + /// When we are in a trait impl, link to the trait-item's id. + pub trait_item_def_id: Option, } #[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)] @@ -2919,7 +2948,6 @@ impl ForeignItemId { #[derive(Debug, HashStable_Generic)] pub struct ForeignItemRef { pub id: ForeignItemId, - #[stable_hasher(project(name))] pub ident: Ident, pub span: Span, } @@ -3189,7 +3217,7 @@ impl<'hir> Node<'hir> { } } - pub fn fn_decl(&self) -> Option<&FnDecl<'hir>> { + pub fn fn_decl(&self) -> Option<&'hir FnDecl<'hir>> { match self { Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) @@ -3201,6 +3229,15 @@ impl<'hir> Node<'hir> { } } + pub fn fn_sig(&self) -> Option<&'hir FnSig<'hir>> { + match self { + Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) + | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig), + _ => None, + } + } + pub fn body_id(&self) -> Option { match self { Node::TraitItem(TraitItem { @@ -3264,13 +3301,13 @@ impl<'hir> Node<'hir> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { rustc_data_structures::static_assert_size!(super::Block<'static>, 48); - rustc_data_structures::static_assert_size!(super::Expr<'static>, 64); + rustc_data_structures::static_assert_size!(super::Expr<'static>, 56); rustc_data_structures::static_assert_size!(super::Pat<'static>, 88); rustc_data_structures::static_assert_size!(super::QPath<'static>, 24); rustc_data_structures::static_assert_size!(super::Ty<'static>, 80); rustc_data_structures::static_assert_size!(super::Item<'static>, 184); rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128); - rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 152); + rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 144); rustc_data_structures::static_assert_size!(super::ForeignItem<'static>, 136); } diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index 1482a96cae..dee391b9cc 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -19,15 +19,22 @@ pub struct HirId { } impl HirId { + #[inline] pub fn expect_owner(self) -> LocalDefId { assert_eq!(self.local_id.index(), 0); self.owner } + #[inline] pub fn as_owner(self) -> Option { if self.local_id.index() == 0 { Some(self.owner) } else { None } } + #[inline] + pub fn is_owner(self) -> bool { + self.local_id.index() == 0 + } + #[inline] pub fn make_owner(owner: LocalDefId) -> Self { Self { owner, local_id: ItemLocalId::from_u32(0) } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d0eee42220..c55f2a7b03 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -141,58 +141,46 @@ pub trait Map<'hir> { // Used when no map is actually available, forcing manual implementation of nested visitors. impl<'hir> Map<'hir> for ! { fn find(&self, _: HirId) -> Option> { - unreachable!() + *self; } fn body(&self, _: BodyId) -> &'hir Body<'hir> { - unreachable!() + *self; } fn item(&self, _: ItemId) -> &'hir Item<'hir> { - unreachable!() + *self; } fn trait_item(&self, _: TraitItemId) -> &'hir TraitItem<'hir> { - unreachable!() + *self; } fn impl_item(&self, _: ImplItemId) -> &'hir ImplItem<'hir> { - unreachable!() + *self; } fn foreign_item(&self, _: ForeignItemId) -> &'hir ForeignItem<'hir> { - unreachable!() + *self; } } -/// An erased version of `Map<'hir>`, using dynamic dispatch. -/// NOTE: This type is effectively only usable with `NestedVisitorMap::None`. -pub struct ErasedMap<'hir>(&'hir dyn Map<'hir>); +pub mod nested_filter { + use super::Map; -impl<'hir> Map<'hir> for ErasedMap<'hir> { - fn find(&self, _: HirId) -> Option> { - None - } - fn body(&self, id: BodyId) -> &'hir Body<'hir> { - self.0.body(id) - } - fn item(&self, id: ItemId) -> &'hir Item<'hir> { - self.0.item(id) - } - fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { - self.0.trait_item(id) - } - fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { - self.0.impl_item(id) - } - fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> { - self.0.foreign_item(id) + /// Specifies what nested things a visitor wants to visit. The most + /// common choice is `OnlyBodies`, which will cause the visitor to + /// visit fn bodies for fns that it encounters, but skip over nested + /// item-like things. + /// + /// See the comments on `ItemLikeVisitor` for more details on the overall + /// visit strategy. + pub trait NestedFilter<'hir> { + type Map: Map<'hir>; + + /// Whether the visitor visits nested "item-like" things. + /// E.g., item, impl-item. + const INTER: bool; + /// Whether the visitor visits "intra item-like" things. + /// E.g., function body, closure, `AnonConst` + const INTRA: bool; } -} -/// Specifies what nested things a visitor wants to visit. The most -/// common choice is `OnlyBodies`, which will cause the visitor to -/// visit fn bodies for fns that it encounters, but skip over nested -/// item-like things. -/// -/// See the comments on `ItemLikeVisitor` for more details on the overall -/// visit strategy. -pub enum NestedVisitorMap { /// Do not visit any nested things. When you add a new /// "non-nested" thing, you will want to audit such uses to see if /// they remain valid. @@ -200,47 +188,16 @@ pub enum NestedVisitorMap { /// Use this if you are only walking some particular kind of tree /// (i.e., a type, or fn signature) and you don't want to thread a /// HIR map around. - None, - - /// Do not visit nested item-like things, but visit nested things - /// that are inside of an item-like. - /// - /// **This is the most common choice.** A very common pattern is - /// to use `visit_all_item_likes()` as an outer loop, - /// and to have the visitor that visits the contents of each item - /// using this setting. - OnlyBodies(M), - - /// Visits all nested things, including item-likes. - /// - /// **This is an unusual choice.** It is used when you want to - /// process everything within their lexical context. Typically you - /// kick off the visit by doing `walk_krate()`. - All(M), -} - -impl NestedVisitorMap { - /// Returns the map to use for an "intra item-like" thing (if any). - /// E.g., function body. - fn intra(self) -> Option { - match self { - NestedVisitorMap::None => None, - NestedVisitorMap::OnlyBodies(map) => Some(map), - NestedVisitorMap::All(map) => Some(map), - } - } - - /// Returns the map to use for an "item-like" thing (if any). - /// E.g., item, impl-item. - fn inter(self) -> Option { - match self { - NestedVisitorMap::None => None, - NestedVisitorMap::OnlyBodies(_) => None, - NestedVisitorMap::All(map) => Some(map), - } + pub struct None(()); + impl NestedFilter<'_> for None { + type Map = !; + const INTER: bool = false; + const INTRA: bool = false; } } +use nested_filter::NestedFilter; + /// Each method of the Visitor trait is a hook to be potentially /// overridden. Each method's default implementation recursively visits /// the substructure of the input via the corresponding `walk` method; @@ -258,7 +215,9 @@ impl NestedVisitorMap { /// to monitor future changes to `Visitor` in case a new method with a /// new default implementation gets introduced.) pub trait Visitor<'v>: Sized { - type Map: Map<'v>; + // this type should not be overridden, it exists for convenient usage as `Self::Map` + type Map: Map<'v> = >::Map; + type NestedFilter: NestedFilter<'v> = nested_filter::None; /////////////////////////////////////////////////////////////////////////// // Nested items. @@ -279,7 +238,12 @@ pub trait Visitor<'v>: Sized { /// `panic!()`. This way, if a new `visit_nested_XXX` variant is /// added in the future, we will see the panic in your code and /// fix it appropriately. - fn nested_visit_map(&mut self) -> NestedVisitorMap; + fn nested_visit_map(&mut self) -> Self::Map { + panic!( + "nested_visit_map must be implemented or consider using \ + `type NestedFilter = nested_filter::None` (the default)" + ); + } /// Invoked when a nested item is encountered. By default does /// nothing unless you override `nested_visit_map` to return other than @@ -290,32 +254,40 @@ pub trait Visitor<'v>: Sized { /// reason to override this method is if you want a nested pattern /// but cannot supply a `Map`; see `nested_visit_map` for advice. fn visit_nested_item(&mut self, id: ItemId) { - let opt_item = self.nested_visit_map().inter().map(|map| map.item(id)); - walk_list!(self, visit_item, opt_item); + if Self::NestedFilter::INTER { + let item = self.nested_visit_map().item(id); + self.visit_item(item); + } } /// Like `visit_nested_item()`, but for trait items. See /// `visit_nested_item()` for advice on when to override this /// method. fn visit_nested_trait_item(&mut self, id: TraitItemId) { - let opt_item = self.nested_visit_map().inter().map(|map| map.trait_item(id)); - walk_list!(self, visit_trait_item, opt_item); + if Self::NestedFilter::INTER { + let item = self.nested_visit_map().trait_item(id); + self.visit_trait_item(item); + } } /// Like `visit_nested_item()`, but for impl items. See /// `visit_nested_item()` for advice on when to override this /// method. fn visit_nested_impl_item(&mut self, id: ImplItemId) { - let opt_item = self.nested_visit_map().inter().map(|map| map.impl_item(id)); - walk_list!(self, visit_impl_item, opt_item); + if Self::NestedFilter::INTER { + let item = self.nested_visit_map().impl_item(id); + self.visit_impl_item(item); + } } /// Like `visit_nested_item()`, but for foreign items. See /// `visit_nested_item()` for advice on when to override this /// method. fn visit_nested_foreign_item(&mut self, id: ForeignItemId) { - let opt_item = self.nested_visit_map().inter().map(|map| map.foreign_item(id)); - walk_list!(self, visit_foreign_item, opt_item); + if Self::NestedFilter::INTER { + let item = self.nested_visit_map().foreign_item(id); + self.visit_foreign_item(item); + } } /// Invoked to visit the body of a function, method or closure. Like @@ -323,8 +295,10 @@ pub trait Visitor<'v>: Sized { /// `nested_visit_map` to return other than `None`, in which case it will walk /// the body. fn visit_nested_body(&mut self, id: BodyId) { - let opt_body = self.nested_visit_map().intra().map(|map| map.body(id)); - walk_list!(self, visit_body, opt_body); + if Self::NestedFilter::INTRA { + let body = self.nested_visit_map().body(id); + self.visit_body(body); + } } fn visit_param(&mut self, param: &'v Param<'v>) { @@ -827,12 +801,11 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>( 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); - } - TypeBindingKind::Constraint { bounds } => { - walk_list!(visitor, visit_param_bound, bounds); - } + TypeBindingKind::Equality { ref term } => match term { + Term::Ty(ref ty) => visitor.visit_ty(ty), + Term::Const(ref c) => visitor.visit_anon_const(c), + }, + TypeBindingKind::Constraint { bounds } => walk_list!(visitor, visit_param_bound, bounds), } } @@ -1047,12 +1020,10 @@ pub fn walk_trait_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_item_ref: pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem<'v>) { // N.B., deliberately force a compilation error if/when new fields are added. - let ImplItem { def_id: _, ident, ref vis, ref defaultness, ref generics, ref kind, span: _ } = - *impl_item; + let ImplItem { def_id: _, ident, ref vis, ref generics, ref kind, span: _ } = *impl_item; visitor.visit_ident(ident); visitor.visit_vis(vis); - visitor.visit_defaultness(defaultness); visitor.visit_generics(generics); match *kind { ImplItemKind::Const(ref ty, body) => { @@ -1088,7 +1059,8 @@ pub fn walk_foreign_item_ref<'v, V: Visitor<'v>>( pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, impl_item_ref: &'v ImplItemRef) { // N.B., deliberately force a compilation error if/when new fields are added. - let ImplItemRef { id, ident, ref kind, span: _, ref defaultness } = *impl_item_ref; + let ImplItemRef { id, ident, ref kind, span: _, ref defaultness, trait_item_def_id: _ } = + *impl_item_ref; visitor.visit_nested_impl_item(id); visitor.visit_ident(ident); visitor.visit_associated_item_kind(kind); @@ -1175,7 +1147,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) visitor.visit_expr(callee_expression); walk_list!(visitor, visit_expr, arguments); } - ExprKind::MethodCall(ref segment, _, arguments, _) => { + ExprKind::MethodCall(ref segment, arguments, _) => { visitor.visit_path_segment(expression.span, segment); walk_list!(visitor, visit_expr, arguments); } @@ -1251,10 +1223,6 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::InlineAsm(ref asm) => { walk_inline_asm(visitor, asm); } - ExprKind::LlvmInlineAsm(ref asm) => { - walk_list!(visitor, visit_expr, asm.outputs_exprs); - walk_list!(visitor, visit_expr, asm.inputs_exprs); - } ExprKind::Yield(ref subexpression, _) => { visitor.visit_expr(subexpression); } diff --git a/compiler/rustc_hir/src/itemlikevisit.rs b/compiler/rustc_hir/src/itemlikevisit.rs index 0db562f91a..db70002c2d 100644 --- a/compiler/rustc_hir/src/itemlikevisit.rs +++ b/compiler/rustc_hir/src/itemlikevisit.rs @@ -8,7 +8,7 @@ use super::{ForeignItem, ImplItem, Item, TraitItem}; /// /// 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR. /// - Example: find all items with a `#[foo]` attribute on them. -/// - How: Implement `ItemLikeVisitor` and call `tcx.hir().krate().visit_all_item_likes()`. +/// - How: Implement `ItemLikeVisitor` and call `tcx.hir().visit_all_item_likes()`. /// - Pro: Efficient; just walks the lists of item-like things, not the nodes themselves. /// - Con: Don't get information about nesting /// - Con: Don't have methods for specific bits of HIR, like "on @@ -19,9 +19,9 @@ use super::{ForeignItem, ImplItem, Item, TraitItem}; /// - Example: Examine each expression to look for its type and do some check or other. /// - How: Implement `intravisit::Visitor` and override the `nested_visit_map()` method /// to return `NestedVisitorMap::OnlyBodies` and use -/// `tcx.hir().krate().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within -/// your `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget -/// to invoke `intravisit::walk_expr()` to keep walking the subparts). +/// `tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within your +/// `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke +/// `intravisit::walk_expr()` to keep walking the subparts). /// - Pro: Visitor methods for any kind of HIR node, not just item-like things. /// - Pro: Integrates well into dependency tracking. /// - Con: Don't get information about nesting between items diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index add8bd1393..b299e71c9c 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -21,9 +21,10 @@ use std::lazy::SyncLazy; pub enum LangItemGroup { Op, + Fn, } -const NUM_GROUPS: usize = 1; +const NUM_GROUPS: usize = 2; macro_rules! expand_group { () => { @@ -98,11 +99,12 @@ macro_rules! language_item_table { /// Construct an empty collection of lang items and no missing ones. pub fn new() -> Self { fn init_none(_: LangItem) -> Option { None } + const EMPTY: Vec = Vec::new(); Self { items: vec![$(init_none(LangItem::$variant)),*], missing: Vec::new(), - groups: [vec![]; NUM_GROUPS], + groups: [EMPTY; NUM_GROUPS], } } @@ -151,20 +153,12 @@ impl HashStable for LangItem { /// Extracts the first `lang = "$name"` out of a list of attributes. /// The attributes `#[panic_handler]` and `#[alloc_error_handler]` /// are also extracted out when found. -/// -/// About the `check_name` argument: passing in a `Session` would be simpler, -/// because then we could call `Session::check_name` directly. But we want to -/// avoid the need for `rustc_hir` to depend on `rustc_session`, so we -/// use a closure instead. -pub fn extract<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<(Symbol, Span)> -where - F: Fn(&'a ast::Attribute, Symbol) -> bool, -{ +pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> { attrs.iter().find_map(|attr| { Some(match attr { - _ if check_name(attr, sym::lang) => (attr.value_str()?, attr.span), - _ if check_name(attr, sym::panic_handler) => (sym::panic_impl, attr.span), - _ if check_name(attr, sym::alloc_error_handler) => (sym::oom, attr.span), + _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span), + _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span), + _ if attr.has_name(sym::alloc_error_handler) => (sym::oom, attr.span), _ => return None, }) }) @@ -259,9 +253,9 @@ language_item_table! { DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; - Fn, kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); - FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); - FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + Fn(Fn), kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); + FnMut(Fn), sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); + FnOnce(Fn), sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; @@ -272,8 +266,8 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; - PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); - PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); + PartialEq(Op), sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); + PartialOrd(Op), sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. @@ -290,6 +284,7 @@ language_item_table! { PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None; PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None; PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; + PanicNoUnwind, sym::panic_no_unwind, panic_no_unwind, Target::Fn, GenericRequirement::Exact(0); /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 1df9b5f9c7..f1d62d03cb 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -2,6 +2,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html +#![feature(associated_type_defaults)] #![feature(const_btree_new)] #![feature(crate_visibility_modifier)] #![feature(once_cell)] diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index a43c1f9d9a..61f03442d6 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -164,13 +164,11 @@ impl HashStable for TraitItem<'_> { impl HashStable for ImplItem<'_> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - let ImplItem { def_id: _, ident, ref vis, defaultness, ref generics, ref kind, span } = - *self; + let ImplItem { def_id: _, ident, ref vis, ref generics, ref kind, span } = *self; hcx.hash_hir_item_like(|hcx| { ident.name.hash_stable(hcx, hasher); vis.hash_stable(hcx, hasher); - defaultness.hash_stable(hcx, hasher); generics.hash_stable(hcx, hasher); kind.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); @@ -208,8 +206,16 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable for OwnerNodes<' fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { // We ignore the `nodes` and `bodies` fields since these refer to information included in // `hash` which is hashed in the collector and used for the crate hash. - let OwnerNodes { hash_including_bodies, hash_without_bodies: _, nodes: _, bodies: _ } = - *self; + // `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing + // the body satisfies the condition of two nodes being different have different + // `hash_stable` results. + let OwnerNodes { + hash_including_bodies, + hash_without_bodies: _, + nodes: _, + bodies: _, + local_id_to_def_id: _, + } = *self; hash_including_bodies.hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 58c3065240..78748209d1 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -18,13 +18,9 @@ pub static WEAK_ITEMS_REFS: SyncLazy> = SyncLazy::ne map }); -/// The `check_name` argument avoids the need for `rustc_hir` to depend on -/// `rustc_session`. -pub fn link_name<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option -where - F: Fn(&'a ast::Attribute, Symbol) -> bool +pub fn link_name(attrs: &[ast::Attribute]) -> Option { - lang_items::extract(check_name, attrs).and_then(|(name, _)| { + lang_items::extract(attrs).and_then(|(name, _)| { $(if name == sym::$name { Some(sym::$sym) } else)* { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 4c9e2d7fe4..8e45b636f4 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -6,7 +6,7 @@ use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir as hir; -use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node}; +use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node, Term}; use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier}; use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::symbol::{kw, Ident, IdentPrinter, Symbol}; @@ -139,7 +139,7 @@ impl<'a> PrintState<'a> for State<'a> { } } -pub const INDENT_UNIT: usize = 4; +pub const INDENT_UNIT: isize = 4; /// Requires you to pass an input filename and reader so that /// it can scan the input text for comments to copy forward. @@ -170,7 +170,7 @@ impl<'a> State<'a> { ann: &'a dyn PpAnn, ) -> State<'a> { State { - s: pp::mk_printer(), + s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), attrs, ann, @@ -186,7 +186,7 @@ pub fn to_string(ann: &dyn PpAnn, f: F) -> String where F: FnOnce(&mut State<'_>), { - let mut printer = State { s: pp::mk_printer(), comments: None, attrs: &|_| &[], ann }; + let mut printer = State { s: pp::Printer::new(), comments: None, attrs: &|_| &[], ann }; f(&mut printer); printer.s.eof() } @@ -571,7 +571,7 @@ impl<'a> State<'a> { self.ann.nested(self, Nested::Body(body)); } hir::ItemKind::Macro(ref macro_def) => { - self.print_mac_def(macro_def, &item.ident, &item.span, |state| { + self.print_mac_def(macro_def, &item.ident, item.span, |state| { state.print_visibility(&item.vis) }); } @@ -705,9 +705,7 @@ impl<'a> State<'a> { self.bclose(item.span); } hir::ItemKind::TraitAlias(ref generics, ref bounds) => { - self.head(""); - self.print_visibility(&item.vis); - self.word_nbsp("trait"); + self.head(visibility_qualified(&item.vis, "trait")); self.print_ident(item.ident); self.print_generic_params(&generics.params); let mut real_bounds = Vec::with_capacity(bounds.len()); @@ -725,6 +723,8 @@ impl<'a> State<'a> { self.print_bounds("=", real_bounds); self.print_where_clause(&generics.where_clause); self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block } } self.ann.post(self, AnnNode::Item(item)) @@ -923,7 +923,6 @@ impl<'a> State<'a> { self.hardbreak_if_not_bol(); self.maybe_print_comment(ii.span.lo()); self.print_outer_attributes(self.attrs(ii.hir_id())); - self.print_defaultness(ii.defaultness); match ii.kind { hir::ImplItemKind::Const(ref ty, expr) => { @@ -1427,7 +1426,7 @@ impl<'a> State<'a> { hir::ExprKind::Call(ref func, ref args) => { self.print_expr_call(&func, args); } - hir::ExprKind::MethodCall(ref segment, _, ref args, _) => { + hir::ExprKind::MethodCall(ref segment, ref args, _) => { self.print_expr_method_call(segment, args); } hir::ExprKind::Binary(op, ref lhs, ref rhs) => { @@ -1581,67 +1580,6 @@ impl<'a> State<'a> { self.word("asm!"); self.print_inline_asm(asm); } - hir::ExprKind::LlvmInlineAsm(ref a) => { - let i = &a.inner; - self.word("llvm_asm!"); - self.popen(); - self.print_symbol(i.asm, i.asm_str_style); - self.word_space(":"); - - let mut out_idx = 0; - self.commasep(Inconsistent, &i.outputs, |s, out| { - let constraint = out.constraint.as_str(); - let mut ch = constraint.chars(); - match ch.next() { - Some('=') if out.is_rw => { - s.print_string(&format!("+{}", ch.as_str()), ast::StrStyle::Cooked) - } - _ => s.print_string(&constraint, ast::StrStyle::Cooked), - } - s.popen(); - s.print_expr(&a.outputs_exprs[out_idx]); - s.pclose(); - out_idx += 1; - }); - self.space(); - self.word_space(":"); - - let mut in_idx = 0; - self.commasep(Inconsistent, &i.inputs, |s, &co| { - s.print_symbol(co, ast::StrStyle::Cooked); - s.popen(); - s.print_expr(&a.inputs_exprs[in_idx]); - s.pclose(); - in_idx += 1; - }); - self.space(); - self.word_space(":"); - - self.commasep(Inconsistent, &i.clobbers, |s, &co| { - s.print_symbol(co, ast::StrStyle::Cooked); - }); - - let mut options = vec![]; - if i.volatile { - options.push("volatile"); - } - if i.alignstack { - options.push("alignstack"); - } - if i.dialect == ast::LlvmAsmDialect::Intel { - options.push("intel"); - } - - if !options.is_empty() { - self.space(); - self.word_space(":"); - self.commasep(Inconsistent, &options, |s, &co| { - s.print_string(co, ast::StrStyle::Cooked); - }); - } - - self.pclose(); - } hir::ExprKind::Yield(ref expr, _) => { self.word_space("yield"); self.print_expr_maybe_paren(&expr, parser::PREC_JUMP); @@ -1813,9 +1751,12 @@ impl<'a> State<'a> { self.print_generic_args(binding.gen_args, false, false); self.space(); match generic_args.bindings[0].kind { - hir::TypeBindingKind::Equality { ref ty } => { + hir::TypeBindingKind::Equality { ref term } => { self.word_space("="); - self.print_type(ty); + match term { + Term::Ty(ref ty) => self.print_type(ty), + Term::Const(ref c) => self.print_anon_const(c), + } } hir::TypeBindingKind::Constraint { bounds } => { self.print_bounds(":", bounds); diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index dece752c19..d3c425a072 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] rustc_graphviz = { path = "../rustc_graphviz" } tracing = "0.1" -rand = "0.7" +rand = "0.8.4" rustc_middle = { path = "../rustc_middle" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 0d0d09bde5..60b48e9bc8 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -39,11 +39,11 @@ use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMIN use rustc_graphviz as dot; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::dep_graph::{ DepGraphQuery, DepKind, DepNode, DepNodeExt, DepNodeFilter, EdgeFilter, }; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; @@ -173,10 +173,10 @@ impl<'tcx> IfThisChanged<'tcx> { } impl<'tcx> Visitor<'tcx> for IfThisChanged<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index df64534ce5..b4df3e1e39 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -5,6 +5,7 @@ #![feature(let_else)] #![feature(nll)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_middle; diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 8879567994..94c149dd23 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -28,7 +28,7 @@ use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::Node as HirNode; use rustc_hir::{ImplItemKind, ItemKind as HirItem, TraitItemKind}; use rustc_middle::dep_graph::{label_strs, DepNode, DepNodeExt}; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; @@ -223,8 +223,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { /// Return all DepNode labels that should be asserted for this item. /// index=0 is the "name" used for error messages fn auto_labels(&mut self, item_id: LocalDefId, attr: &Attribute) -> (&'static str, Labels) { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(item_id); - let node = self.tcx.hir().get(hir_id); + let node = self.tcx.hir().get_by_def_id(item_id); let (name, labels) = match node { HirNode::Item(item) => { match item.kind { @@ -473,10 +472,10 @@ impl<'tcx> FindAllAttrs<'tcx> { } impl<'tcx> intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_attribute(&mut self, _: hir::HirId, attr: &'tcx Attribute) { diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index 392c5bdc15..68180a2214 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -190,7 +190,7 @@ fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: & fn rustc_version(nightly_build: bool) -> String { if nightly_build { - if let Some(val) = env::var_os("RUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER") { + if let Some(val) = env::var_os("RUSTC_FORCE_RUSTC_VERSION") { return val.to_string_lossy().into_owned(); } } diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 652ef6bcdc..184796948b 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -13,7 +13,7 @@ fn test_all_except_most_recent() { .keys() .cloned() .collect::>(), - vec![PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4"),] + [PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4"),] .into_iter() .collect::>() ); @@ -40,7 +40,7 @@ fn test_find_source_directory_in_iter() { // Find newest assert_eq!( find_source_directory_in_iter( - vec![ + [ PathBuf::from("crate-dir/s-3234-0000-svh"), PathBuf::from("crate-dir/s-2234-0000-svh"), PathBuf::from("crate-dir/s-1234-0000-svh") @@ -54,7 +54,7 @@ fn test_find_source_directory_in_iter() { // Filter out "-working" assert_eq!( find_source_directory_in_iter( - vec![ + [ PathBuf::from("crate-dir/s-3234-0000-working"), PathBuf::from("crate-dir/s-2234-0000-svh"), PathBuf::from("crate-dir/s-1234-0000-svh") @@ -66,12 +66,12 @@ fn test_find_source_directory_in_iter() { ); // Handle empty - assert_eq!(find_source_directory_in_iter(vec![].into_iter(), &already_visited), None); + assert_eq!(find_source_directory_in_iter([].into_iter(), &already_visited), None); // Handle only working assert_eq!( find_source_directory_in_iter( - vec![ + [ PathBuf::from("crate-dir/s-3234-0000-working"), PathBuf::from("crate-dir/s-2234-0000-working"), PathBuf::from("crate-dir/s-1234-0000-working") diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index d563a6ca47..870c3f8068 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -158,14 +158,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { // Decode the list of work_products let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos); let work_products: Vec = - Decodable::decode(&mut work_product_decoder).unwrap_or_else(|e| { - let msg = format!( - "Error decoding `work-products` from incremental \ - compilation session directory: {}", - e - ); - sess.fatal(&msg) - }); + Decodable::decode(&mut work_product_decoder); for swp in work_products { let mut all_files_exist = true; @@ -203,8 +196,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { LoadResult::Error { message } => LoadResult::Error { message }, LoadResult::Ok { data: (bytes, start_pos) } => { let mut decoder = Decoder::new(&bytes, start_pos); - let prev_commandline_args_hash = u64::decode(&mut decoder) - .expect("Error reading commandline arg hash from cached dep-graph"); + let prev_commandline_args_hash = u64::decode(&mut decoder); if prev_commandline_args_hash != expected_hash { if report_incremental_info { @@ -220,8 +212,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { return LoadResult::DataOutOfDate; } - let dep_graph = SerializedDepGraph::decode(&mut decoder) - .expect("Error reading cached dep-graph"); + let dep_graph = SerializedDepGraph::decode(&mut decoder); LoadResult::Ok { data: (dep_graph, prev_work_products) } } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 5aa213cb70..7f376c5fbe 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -852,11 +852,7 @@ impl HybridBitSet { Bound::Excluded(end) => end.index(), Bound::Unbounded => self.domain_size() - 1, }; - let len = if let Some(l) = end.checked_sub(start) { - l - } else { - return; - }; + let Some(len) = end.checked_sub(start) else { return }; match self { HybridBitSet::Sparse(sparse) if sparse.len() + len < SPARSE_MAX => { // The set is sparse and has space for `elems`. @@ -938,6 +934,12 @@ pub struct GrowableBitSet { bit_set: BitSet, } +impl Default for GrowableBitSet { + fn default() -> Self { + GrowableBitSet::new_empty() + } +} + impl GrowableBitSet { /// Ensure that the set can hold at least `min_domain_size` elements. pub fn ensure(&mut self, min_domain_size: usize) { diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index 359b1859c6..7919e40925 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -9,7 +9,3 @@ pub mod bit_set; pub mod interval; pub mod vec; - -// FIXME(#56935): Work around ICEs during cross-compilation. -#[allow(unused)] -extern crate rustc_macros; diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index e3c6528b21..8b61530577 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -395,8 +395,8 @@ macro_rules! newtype_index { (@serializable $type:ident) => ( impl ::rustc_serialize::Decodable for $type { - fn decode(d: &mut D) -> Result { - d.read_u32().map(Self::from_u32) + fn decode(d: &mut D) -> Self { + Self::from_u32(d.read_u32()) } } impl ::rustc_serialize::Encodable for $type { @@ -527,8 +527,8 @@ impl> Encodable for &IndexVec { } impl> Decodable for IndexVec { - fn decode(d: &mut D) -> Result { - Decodable::decode(d).map(|v| IndexVec { raw: v, _marker: PhantomData }) + fn decode(d: &mut D) -> Self { + IndexVec { raw: Decodable::decode(d), _marker: PhantomData } } } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index cff848eeb6..94991fdb20 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -51,6 +51,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) -> At<'a, 'tcx> { At { infcx: self, cause, param_env } } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state. This can be used to "branch off" many tests from the same + /// common state. Used in coherence. + pub fn fork(&self) -> Self { + Self { + tcx: self.tcx.clone(), + defining_use_anchor: self.defining_use_anchor.clone(), + in_progress_typeck_results: self.in_progress_typeck_results.clone(), + inner: self.inner.clone(), + skip_leak_check: self.skip_leak_check.clone(), + lexical_region_resolutions: self.lexical_region_resolutions.clone(), + selection_cache: self.selection_cache.clone(), + evaluation_cache: self.evaluation_cache.clone(), + reported_trait_errors: self.reported_trait_errors.clone(), + reported_closure_mismatch: self.reported_closure_mismatch.clone(), + tainted_by_errors_flag: self.tainted_by_errors_flag.clone(), + err_count_on_creation: self.err_count_on_creation, + in_snapshot: self.in_snapshot.clone(), + universe: self.universe.clone(), + } + } } pub trait ToTrace<'tcx>: Relate<'tcx> + Copy { @@ -258,7 +280,10 @@ impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { a: Self, b: Self, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } } } @@ -274,7 +299,22 @@ impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> { } } -impl<'tcx> ToTrace<'tcx> for &'tcx Const<'tcx> { +impl<'tcx> ToTrace<'tcx> for Const<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } + } +} + +impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> { fn to_trace( _: TyCtxt<'tcx>, cause: &ObligationCause<'tcx>, @@ -282,7 +322,7 @@ impl<'tcx> ToTrace<'tcx> for &'tcx Const<'tcx> { a: Self, b: Self, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { cause: cause.clone(), values: Terms(ExpectedFound::new(a_is_expected, a, b)) } } } @@ -328,7 +368,7 @@ impl<'tcx> ToTrace<'tcx> for ty::ProjectionTy<'tcx> { let b_ty = tcx.mk_projection(b.item_def_id, b.substs); TypeTrace { cause: cause.clone(), - values: Types(ExpectedFound::new(a_is_expected, a_ty, b_ty)), + values: Terms(ExpectedFound::new(a_is_expected, a_ty.into(), b_ty.into())), } } } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 6023973665..5e67c8cfa2 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -49,6 +49,31 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state) } + /// Like [Self::canonicalize_query], but preserves distinct universes. For + /// example, canonicalizing `&'?0: Trait<'?1>`, where `'?0` is in `U1` and + /// `'?1` is in `U3` would be canonicalized to have ?0` in `U1` and `'?1` + /// in `U2`. + /// + /// This is used for Chalk integration. + pub fn canonicalize_query_preserving_universes( + &self, + value: V, + query_state: &mut OriginalQueryValues<'tcx>, + ) -> Canonicalized<'tcx, V> + where + V: TypeFoldable<'tcx>, + { + self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); + + Canonicalizer::canonicalize( + value, + self, + self.tcx, + &CanonicalizeAllFreeRegionsPreservingUniverses, + query_state, + ) + } + /// Canonicalizes a query *response* `V`. When we canonicalize a /// query response, we only canonicalize unbound inference /// variables, and we leave other free regions alone. So, @@ -133,7 +158,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// maximally general query. But if we are canonicalizing a *query /// response*, then we don't typically replace free regions, as they /// must have been introduced from other parts of the system. -trait CanonicalizeRegionMode { +trait CanonicalizeMode { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, @@ -141,17 +166,20 @@ trait CanonicalizeRegionMode { ) -> ty::Region<'tcx>; fn any(&self) -> bool; + + // Do we preserve universe of variables. + fn preserve_universes(&self) -> bool; } struct CanonicalizeQueryResponse; -impl CanonicalizeRegionMode for CanonicalizeQueryResponse { +impl CanonicalizeMode for CanonicalizeQueryResponse { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - match r { + match *r { ty::ReFree(_) | ty::ReErased | ty::ReStatic @@ -159,12 +187,12 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { | ty::ReEarlyBound(..) => r, ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( - CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder) }, + CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(placeholder) }, r, ), ty::ReVar(vid) => { - let universe = canonicalizer.region_var_universe(*vid); + let universe = canonicalizer.region_var_universe(vid); canonicalizer.canonical_var_for_region( CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) }, r, @@ -198,17 +226,21 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { fn any(&self) -> bool { false } + + fn preserve_universes(&self) -> bool { + true + } } struct CanonicalizeUserTypeAnnotation; -impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { +impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - match r { + match *r { ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r, ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), _ => { @@ -221,11 +253,15 @@ impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { fn any(&self) -> bool { false } + + fn preserve_universes(&self) -> bool { + false + } } struct CanonicalizeAllFreeRegions; -impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { +impl CanonicalizeMode for CanonicalizeAllFreeRegions { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, @@ -237,26 +273,54 @@ impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { fn any(&self) -> bool { true } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct CanonicalizeAllFreeRegionsPreservingUniverses; + +impl CanonicalizeMode for CanonicalizeAllFreeRegionsPreservingUniverses { + fn canonicalize_free_region<'tcx>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + let universe = canonicalizer.infcx.universe_of_region(r); + canonicalizer.canonical_var_for_region( + CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) }, + r, + ) + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + true + } } struct CanonicalizeFreeRegionsOtherThanStatic; -impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic { +impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic { fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - if let ty::ReStatic = r { - r - } else { - canonicalizer.canonical_var_for_region_in_root_universe(r) - } + if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) } } fn any(&self) -> bool { true } + + fn preserve_universes(&self) -> bool { + false + } } struct Canonicalizer<'cx, 'tcx> { @@ -267,7 +331,7 @@ struct Canonicalizer<'cx, 'tcx> { // Note that indices is only used once `var_values` is big enough to be // heap-allocated. indices: FxHashMap, BoundVar>, - canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode, + canonicalize_mode: &'cx dyn CanonicalizeMode, needs_canonical_flags: TypeFlags, binder_index: ty::DebruijnIndex, @@ -311,7 +375,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { vid, r ); let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid)); - self.canonicalize_region_mode.canonicalize_free_region(self, r) + self.canonicalize_mode.canonicalize_free_region(self, r) } ty::ReStatic @@ -319,7 +383,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { | ty::ReFree(_) | ty::ReEmpty(_) | ty::RePlaceholder(..) - | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r), + | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), } } @@ -337,8 +401,10 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // `TyVar(vid)` is unresolved, track its universe index in the canonicalized // result. Err(mut ui) => { - // FIXME: perf problem described in #55921. - ui = ty::UniverseIndex::ROOT; + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } self.canonicalize_ty_var( CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), @@ -409,8 +475,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { } } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - match ct.val { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.val() { ty::ConstKind::Infer(InferConst::Var(vid)) => { debug!("canonical: const var found with vid {:?}", vid); match self.infcx.probe_const_var(vid) { @@ -422,10 +488,12 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // `ConstVar(vid)` is unresolved, track its universe index in the // canonicalized result Err(mut ui) => { - // FIXME: perf problem described in #55921. - ui = ty::UniverseIndex::ROOT; + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } return self.canonicalize_const_var( - CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) }, + CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty()) }, ct, ); } @@ -462,7 +530,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { value: V, infcx: &InferCtxt<'_, 'tcx>, tcx: TyCtxt<'tcx>, - canonicalize_region_mode: &dyn CanonicalizeRegionMode, + canonicalize_region_mode: &dyn CanonicalizeMode, query_state: &mut OriginalQueryValues<'tcx>, ) -> Canonicalized<'tcx, V> where @@ -470,7 +538,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { { let needs_canonical_flags = if canonicalize_region_mode.any() { TypeFlags::NEEDS_INFER | - TypeFlags::HAS_POTENTIAL_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_POTENTIAL_FREE_REGIONS` + TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS` TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER } else { @@ -493,7 +561,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { let mut canonicalizer = Canonicalizer { infcx, tcx, - canonicalize_region_mode, + canonicalize_mode: canonicalize_region_mode, needs_canonical_flags, variables: SmallVec::new(), query_state, @@ -504,10 +572,11 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { // Once we have canonicalized `out_value`, it should not // contain anything that ties it to this inference context - // anymore, so it should live in the global arena. - debug_assert!(!out_value.needs_infer()); + // anymore. + debug_assert!(!out_value.needs_infer() && !out_value.has_placeholders()); - let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables); + let canonical_variables = + tcx.intern_canonical_var_infos(&canonicalizer.universe_canonicalized_variables()); let max_universe = canonical_variables .iter() @@ -527,6 +596,19 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { let var_values = &mut query_state.var_values; + let universe = info.universe(); + if universe != ty::UniverseIndex::ROOT { + assert!(self.canonicalize_mode.preserve_universes()); + + // Insert universe into the universe map. To preserve the order of the + // universes in the value being canonicalized, we don't update the + // universe in `info` until we have finished canonicalizing. + match query_state.universe_map.binary_search(&universe) { + Err(idx) => query_state.universe_map.insert(idx, universe), + Ok(_) => {} + } + } + // This code is hot. `variables` and `var_values` are usually small // (fewer than 8 elements ~95% of the time). They are SmallVec's to // avoid allocations in those cases. We also don't use `indices` to @@ -569,6 +651,61 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { } } + /// Replaces the universe indexes used in `var_values` with their index in + /// `query_state.universe_map`. This minimizes the maximum universe used in + /// the canonicalized value. + fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarInfo<'tcx>; 8]> { + if self.query_state.universe_map.len() == 1 { + return self.variables; + } + + let reverse_universe_map: FxHashMap = self + .query_state + .universe_map + .iter() + .enumerate() + .map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx))) + .collect(); + + self.variables + .iter() + .map(|v| CanonicalVarInfo { + kind: match v.kind { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + return *v; + } + CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { + CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) + } + CanonicalVarKind::Region(u) => { + CanonicalVarKind::Region(reverse_universe_map[&u]) + } + CanonicalVarKind::Const(u, t) => { + CanonicalVarKind::Const(reverse_universe_map[&u], t) + } + CanonicalVarKind::PlaceholderTy(placeholder) => { + CanonicalVarKind::PlaceholderTy(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderRegion(placeholder) => { + CanonicalVarKind::PlaceholderRegion(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderConst(placeholder) => { + CanonicalVarKind::PlaceholderConst(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + }, + }) + .collect() + } + /// Shorthand helper that creates a canonical region variable for /// `r` (always in the root universe). The reason that we always /// put these variables into the root universe is because this @@ -632,17 +769,17 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { fn canonicalize_const_var( &mut self, info: CanonicalVarInfo<'tcx>, - const_var: &'tcx ty::Const<'tcx>, - ) -> &'tcx ty::Const<'tcx> { + const_var: ty::Const<'tcx>, + ) -> ty::Const<'tcx> { let infcx = self.infcx; let bound_to = infcx.shallow_resolve(const_var); if bound_to != const_var { self.fold_const(bound_to) } else { let var = self.canonical_var(info, const_var.into()); - self.tcx().mk_const(ty::Const { + self.tcx().mk_const(ty::ConstS { val: ty::ConstKind::Bound(self.binder_index, var), - ty: self.fold_ty(const_var.ty), + ty: self.fold_ty(const_var.ty()), }) } } diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 0c26639e9b..7985686798 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -137,12 +137,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into() } - CanonicalVarKind::Const(ui) => self + CanonicalVarKind::Const(ui, ty) => self .next_const_var_in_universe( - self.next_ty_var_in_universe( - TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }, - universe_map(ui), - ), + ty, ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span }, universe_map(ui), ) @@ -152,7 +149,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let universe_mapped = universe_map(universe); let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, name }; self.tcx - .mk_const(ty::Const { + .mk_const(ty::ConstS { val: ty::ConstKind::Placeholder(placeholder_mapped), ty: name.ty, }) diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 5b4a9d9dfa..48d5c21f9e 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -237,10 +237,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { v.var_values[BoundVar::new(index)] }); match (original_value.unpack(), result_value.unpack()) { - ( - GenericArgKind::Lifetime(ty::ReErased), - GenericArgKind::Lifetime(ty::ReErased), - ) => { + (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2)) + if re1.is_erased() && re2.is_erased() => + { // No action needed. } @@ -429,7 +428,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } GenericArgKind::Lifetime(result_value) => { // e.g., here `result_value` might be `'?1` in the example above... - if let &ty::RegionKind::ReLateBound(debruijn, br) = result_value { + if let ty::ReLateBound(debruijn, br) = *result_value { // ... in which case we would set `canonical_vars[0]` to `Some('static)`. // We only allow a `ty::INNERMOST` index in substitutions. @@ -438,12 +437,12 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } } GenericArgKind::Const(result_value) => { - if let ty::Const { val: ty::ConstKind::Bound(debrujin, b), .. } = result_value { + if let ty::ConstKind::Bound(debrujin, b) = result_value.val() { // ...in which case we would set `canonical_vars[0]` to `Some(const X)`. // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(*debrujin, ty::INNERMOST); - opt_values[*b] = Some(*original_value); + assert_eq!(debrujin, ty::INNERMOST); + opt_values[b] = Some(*original_value); } } } @@ -558,10 +557,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { obligations .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); } - ( - GenericArgKind::Lifetime(ty::ReErased), - GenericArgKind::Lifetime(ty::ReErased), - ) => { + (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2)) + if re1.is_erased() && re2.is_erased() => + { // no action needed } (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => { @@ -672,7 +670,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { }); } - fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) { + fn const_equate(&mut self, _a: Const<'tcx>, _b: Const<'tcx>) { span_bug!( self.cause.span(self.infcx.tcx), "generic_const_exprs: unreachable `const_equate`" diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index da71edbd2d..e1b5d04ccf 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -123,14 +123,12 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { pub fn super_combine_consts( &self, relation: &mut R, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> where R: ConstEquateRelation<'tcx>, { - let a = self.tcx.expose_default_const_substs(a); - let b = self.tcx.expose_default_const_substs(b); debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); if a == b { return Ok(a); @@ -141,7 +139,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { let a_is_expected = relation.a_is_expected(); - match (a.val, b.val) { + match (a.val(), b.val()) { ( ty::ConstKind::Infer(InferConst::Var(a_vid)), ty::ConstKind::Infer(InferConst::Var(b_vid)), @@ -228,9 +226,9 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { &self, param_env: ty::ParamEnv<'tcx>, target_vid: ty::ConstVid<'tcx>, - ct: &'tcx ty::Const<'tcx>, + ct: ty::Const<'tcx>, vid_is_expected: bool, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + ) -> RelateResult<'tcx, ty::Const<'tcx>> { let (for_universe, span) = { let mut inner = self.inner.borrow_mut(); let variable_table = &mut inner.const_unification_table(); @@ -453,8 +451,8 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { pub fn add_const_equate_obligation( &mut self, a_is_expected: bool, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, ) { let predicate = if a_is_expected { ty::PredicateKind::ConstEquate(a, b) @@ -718,12 +716,12 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { fn consts( &mut self, - c: &'tcx ty::Const<'tcx>, - c2: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + c: ty::Const<'tcx>, + c2: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { assert_eq!(c, c2); // we are abusing TypeRelation here; both LHS and RHS ought to be == - match c.val { + match c.val() { ty::ConstKind::Infer(InferConst::Var(vid)) => { let mut inner = self.infcx.inner.borrow_mut(); let variable_table = &mut inner.const_unification_table(); @@ -741,23 +739,24 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { origin: var_value.origin, val: ConstVariableValue::Unknown { universe: self.for_universe }, }); - Ok(self.tcx().mk_const_var(new_var_id, c.ty)) + Ok(self.tcx().mk_const_var(new_var_id, c.ty())) } } } } - ty::ConstKind::Unevaluated(uv) if self.tcx().lazy_normalization() => { - assert_eq!(uv.promoted, None); - let substs = uv.substs(self.tcx()); + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if self.tcx().lazy_normalization() => + { + assert_eq!(promoted, None); let substs = self.relate_with_variance( ty::Variance::Invariant, ty::VarianceDiagInfo::default(), substs, substs, )?; - Ok(self.tcx().mk_const(ty::Const { - ty: c.ty, - val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(uv.def, substs)), + Ok(self.tcx().mk_const(ty::ConstS { + ty: c.ty(), + val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), })) } _ => relate::super_relate_consts(self, c, c), @@ -769,7 +768,7 @@ pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> { /// Register an obligation that both constants must be equal to each other. /// /// If they aren't equal then the relation doesn't hold. - fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>); + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); } pub trait RelateResultCompare<'tcx, T> { @@ -789,7 +788,7 @@ impl<'tcx, T: Clone + PartialEq> RelateResultCompare<'tcx, T> for RelateResult<' pub fn const_unification_error<'tcx>( a_is_expected: bool, - (a, b): (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>), + (a, b): (ty::Const<'tcx>, ty::Const<'tcx>), ) -> TypeError<'tcx> { TypeError::ConstMismatch(ExpectedFound::new(a_is_expected, a, b)) } @@ -916,7 +915,7 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { debug_assert_eq!(r, _r); debug!("ConstInferUnifier: r={:?}", r); - match r { + match *r { // Never make variables for regions bound within the type itself, // nor for erased regions. ty::ReLateBound(..) | ty::ReErased => { @@ -946,13 +945,13 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { #[tracing::instrument(level = "debug", skip(self))] fn consts( &mut self, - c: &'tcx ty::Const<'tcx>, - _c: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + c: ty::Const<'tcx>, + _c: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { debug_assert_eq!(c, _c); debug!("ConstInferUnifier: c={:?}", c); - match c.val { + match c.val() { ty::ConstKind::Infer(InferConst::Var(vid)) => { // Check if the current unification would end up // unifying `target_vid` with a const which contains @@ -986,23 +985,24 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { }, }, ); - Ok(self.tcx().mk_const_var(new_var_id, c.ty)) + Ok(self.tcx().mk_const_var(new_var_id, c.ty())) } } } } - ty::ConstKind::Unevaluated(uv) if self.tcx().lazy_normalization() => { - assert_eq!(uv.promoted, None); - let substs = uv.substs(self.tcx()); + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if self.tcx().lazy_normalization() => + { + assert_eq!(promoted, None); let substs = self.relate_with_variance( ty::Variance::Invariant, ty::VarianceDiagInfo::default(), substs, substs, )?; - Ok(self.tcx().mk_const(ty::Const { - ty: c.ty, - val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(uv.def, substs)), + Ok(self.tcx().mk_const(ty::ConstS { + ty: c.ty(), + val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), })) } _ => relate::super_relate_consts(self, c, c), diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 90c0ff9226..5ac9ad6850 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -117,9 +117,9 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { self.fields.infcx.super_combine_consts(self, a, b) } @@ -143,7 +143,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { } impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { self.fields.add_const_equate_obligation(self.a_is_expected, a, b); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index f0c73d0c2f..d900379c44 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -65,11 +65,11 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{Item, ItemKind, Node}; use rustc_middle::dep_graph::DepContext; -use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, + error::TypeError, subst::{GenericArgKind, Subst, SubstsRef}, - Region, Ty, TyCtxt, TypeFoldable, + Binder, Region, Ty, TyCtxt, TypeFoldable, }; use rustc_span::{sym, BytePos, DesugaringKind, MultiSpan, Pos, Span}; use rustc_target::spec::abi; @@ -151,11 +151,10 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>( ) -> (String, Span) { let sm = tcx.sess.source_map(); - let scope = region.free_region_binding_scope(tcx); - let node = tcx.hir().local_def_id_to_hir_id(scope.expect_local()); + let scope = region.free_region_binding_scope(tcx).expect_local(); match *region { ty::ReEarlyBound(ref br) => { - let mut sp = sm.guess_head_span(tcx.hir().span(node)); + let mut sp = sm.guess_head_span(tcx.def_span(scope)); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) { @@ -166,7 +165,7 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>( ty::ReFree(ty::FreeRegion { bound_region: ty::BoundRegionKind::BrNamed(_, name), .. }) => { - let mut sp = sm.guess_head_span(tcx.hir().span(node)); + let mut sp = sm.guess_head_span(tcx.def_span(scope)); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) { @@ -181,13 +180,13 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>( } else { ( format!("the anonymous lifetime #{} defined here", idx + 1), - tcx.hir().span(node), + tcx.def_span(scope), ) } } _ => ( format!("the lifetime `{}` as defined here", region), - sm.guess_head_span(tcx.hir().span(node)), + sm.guess_head_span(tcx.def_span(scope)), ), }, _ => bug!(), @@ -240,7 +239,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( ); // Explain the region we are capturing. - match hidden_region { + match *hidden_region { ty::ReEmpty(ty::UniverseIndex::ROOT) => { // All lifetimes shorter than the function body are `empty` in // lexical region resolution. The default explanation of "an empty @@ -516,7 +515,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Err(NonTrivialPath) } - fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result { + fn print_const(self, _ct: ty::Const<'tcx>) -> Result { Err(NonTrivialPath) } @@ -771,7 +770,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.suggest_boxing_for_return_impl_trait( err, ret_sp, - vec![then, else_sp].into_iter(), + [then, else_sp].into_iter(), ); } } @@ -807,11 +806,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); let sugg = arm_spans .flat_map(|sp| { - vec![ - (sp.shrink_to_lo(), "Box::new(".to_string()), - (sp.shrink_to_hi(), ")".to_string()), - ] - .into_iter() + [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())] + .into_iter() }) .collect::>(); err.multipart_suggestion( @@ -919,13 +915,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) -> Option<()> { for (i, ta) in sub.types().enumerate() { if ta == other_ty { - self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); + self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, other_ty); return Some(()); } 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); + self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, other_ty); return Some(()); } } @@ -1040,7 +1036,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let len2 = sig2.inputs().len(); if len1 == len2 { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { - let (x1, x2) = self.cmp(l, r); + let (x1, x2) = self.cmp(*l, *r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); self.push_comma(&mut values.0, &mut values.1, len1, i); @@ -1118,7 +1114,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } fn push_ty_ref<'tcx>( - region: &ty::Region<'tcx>, + region: ty::Region<'tcx>, ty: Ty<'tcx>, mutbl: hir::Mutability, s: &mut DiagnosticStyledString, @@ -1267,7 +1263,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { path1.clone(), sub_no_defaults_1, path2.clone(), - &t2, + t2, ) .is_some() { @@ -1285,7 +1281,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { path2, sub_no_defaults_2, path1, - &t1, + t1, ) .is_some() { @@ -1337,26 +1333,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } // When finding T != &T, highlight only the borrow - (&ty::Ref(r1, ref_ty1, mutbl1), _) if equals(&ref_ty1, &t2) => { + (&ty::Ref(r1, ref_ty1, mutbl1), _) if equals(ref_ty1, t2) => { let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); - push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0); + push_ty_ref(r1, ref_ty1, mutbl1, &mut values.0); values.1.push_normal(t2.to_string()); values } - (_, &ty::Ref(r2, ref_ty2, mutbl2)) if equals(&t1, &ref_ty2) => { + (_, &ty::Ref(r2, ref_ty2, mutbl2)) if equals(t1, ref_ty2) => { let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); values.0.push_normal(t1.to_string()); - push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1); + push_ty_ref(r2, ref_ty2, mutbl2, &mut values.1); values } // When encountering &T != &mut T, highlight only the borrow (&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2)) - if equals(&ref_ty1, &ref_ty2) => + if equals(ref_ty1, ref_ty2) => { let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); - push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0); - push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1); + push_ty_ref(r1, ref_ty1, mutbl1, &mut values.0); + push_ty_ref(r2, ref_ty2, mutbl2, &mut values.1); values } @@ -1553,10 +1549,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) { let span = self.tcx.def_span(def_id); @@ -1590,18 +1582,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { None => (None, Mismatch::Fixed("type"), false), Some(values) => { let (is_simple_error, exp_found) = match values { - ValuePairs::Types(exp_found) => { - let is_simple_err = - exp_found.expected.is_simple_text() && exp_found.found.is_simple_text(); - OpaqueTypesVisitor::visit_expected_found( - self.tcx, - exp_found.expected, - exp_found.found, - span, - ) - .report(diag); + ValuePairs::Terms(infer::ExpectedFound { + expected: ty::Term::Ty(expected), + found: ty::Term::Ty(found), + }) => { + let is_simple_err = expected.is_simple_text() && found.is_simple_text(); + OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span) + .report(diag); - (is_simple_err, Mismatch::Variable(exp_found)) + ( + is_simple_err, + Mismatch::Variable(infer::ExpectedFound { expected, found }), + ) } ValuePairs::TraitRefs(_) => (false, Mismatch::Fixed("trait")), _ => (false, Mismatch::Fixed("type")), @@ -1632,7 +1624,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; if let Some((sp, msg)) = secondary_span { if swap_secondary_and_primary { - let terr = if let Some(infer::ValuePairs::Types(infer::ExpectedFound { + let terr = if let Some(infer::ValuePairs::Terms(infer::ExpectedFound { expected, .. })) = values @@ -1762,8 +1754,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { 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); + let span = self.tcx.def_span(def_id); diag.span_note(span, "this closure does not fulfill the lifetime requirements"); } } @@ -1774,7 +1765,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.note_error_origin(diag, cause, exp_found, terr); } - pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option> { + pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option>> { if let ty::Opaque(def_id, substs) = ty.kind() { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); // Future::Output @@ -1784,13 +1775,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { for (predicate, _) in bounds { let predicate = predicate.subst(self.tcx, substs); - 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. - return Some(projection_predicate.ty); - } + let output = predicate + .kind() + .map_bound(|kind| match kind { + ty::PredicateKind::Projection(projection_predicate) + if projection_predicate.projection_ty.item_def_id == item_def_id => + { + projection_predicate.term.ty() + } + _ => None, + }) + .transpose(); + if output.is_some() { + // We don't account for multiple `Future::Output = Ty` contraints. + return output; } } } @@ -1832,8 +1830,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } match ( - self.get_impl_future_output_ty(exp_found.expected), - self.get_impl_future_output_ty(exp_found.found), + self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder), + self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder), ) { (Some(exp), Some(found)) if same_type_modulo_infer(exp, found) => match cause.code() { ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => { @@ -1924,8 +1922,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .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)| same_type_modulo_infer(ty, exp_found.found)) + .map(|field| (field.name, field.ty(self.tcx, expected_substs))) + .find(|(_, ty)| same_type_modulo_infer(*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) { @@ -2038,27 +2036,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } FailureCode::Error0308(failure_str) => { let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str); - if let ValuePairs::Types(ty::error::ExpectedFound { expected, found }) = - trace.values - { + if let Some((expected, found)) = trace.values.ty() { match (expected.kind(), found.kind()) { (ty::Tuple(_), ty::Tuple(_)) => {} // If a tuple of length one was expected and the found expression has // parentheses around it, perhaps the user meant to write `(expr,)` to // build a tuple (issue #86100) - (ty::Tuple(_), _) if expected.tuple_fields().count() == 1 => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { - if let Some(code) = - code.strip_prefix('(').and_then(|s| s.strip_suffix(')')) - { - err.span_suggestion( - span, - "use a trailing comma to create a tuple with one element", - format!("({},)", code), - Applicability::MaybeIncorrect, - ); - } - } + (ty::Tuple(_), _) => { + self.emit_tuple_wrap_err(&mut err, span, found, expected) } // If a character was expected and the found expression is a string literal // containing a single character, perhaps the user meant to write `'c'` to @@ -2068,7 +2053,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"')) { - if code.chars().nth(1).is_none() { + if code.chars().count() == 1 { err.span_suggestion( span, "if you meant to write a `char` literal, use single quotes", @@ -2121,14 +2106,48 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { diag } + fn emit_tuple_wrap_err( + &self, + err: &mut DiagnosticBuilder<'tcx>, + span: Span, + found: Ty<'tcx>, + expected: Ty<'tcx>, + ) { + let [expected_tup_elem] = &expected.tuple_fields().collect::>()[..] + else { return }; + + if !same_type_modulo_infer(*expected_tup_elem, found) { + return; + } + + let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + else { return }; + + let msg = "use a trailing comma to create a tuple with one element"; + if code.starts_with('(') && code.ends_with(')') { + let before_close = span.hi() - BytePos::from_u32(1); + err.span_suggestion( + span.with_hi(before_close).shrink_to_hi(), + msg, + ",".into(), + Applicability::MachineApplicable, + ); + } else { + err.multipart_suggestion( + msg, + vec![(span.shrink_to_lo(), "(".into()), (span.shrink_to_hi(), ",)".into())], + Applicability::MachineApplicable, + ); + } + } + fn values_str( &self, values: ValuePairs<'tcx>, ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { match values { - infer::Types(exp_found) => self.expected_found_str_ty(exp_found), infer::Regions(exp_found) => self.expected_found_str(exp_found), - infer::Consts(exp_found) => self.expected_found_str(exp_found), + infer::Terms(exp_found) => self.expected_found_str_term(exp_found), infer::TraitRefs(exp_found) => { let pretty_exp_found = ty::error::ExpectedFound { expected: exp_found.expected.print_only_trait_path(), @@ -2156,16 +2175,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn expected_found_str_ty( + fn expected_found_str_term( &self, - exp_found: ty::error::ExpectedFound>, + exp_found: ty::error::ExpectedFound>, ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { let exp_found = self.resolve_vars_if_possible(exp_found); if exp_found.references_error() { return None; } - Some(self.cmp(exp_found.expected, exp_found.found)) + Some(match (exp_found.expected, exp_found.found) { + (ty::Term::Ty(expected), ty::Term::Ty(found)) => self.cmp(expected, found), + (expected, found) => ( + DiagnosticStyledString::highlighted(expected.to_string()), + DiagnosticStyledString::highlighted(found.to_string()), + ), + }) } /// Returns a string of the form "expected `{}`, found `{}`". @@ -2201,7 +2226,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, ) -> DiagnosticBuilder<'a> { - let hir = &self.tcx.hir(); + let hir = self.tcx.hir(); // Attempt to obtain the span of the parameter so we can // suggest adding an explicit lifetime bound to it. let generics = self @@ -2215,9 +2240,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(Node::Item(Item { kind: ItemKind::Trait(..) | ItemKind::Impl { .. }, .. - })) = hir.find(parent_id) + })) = hir.find_by_def_id(parent_id) { - Some(self.tcx.generics_of(hir.local_def_id(parent_id).to_def_id())) + Some(self.tcx.generics_of(parent_id)) } else { None }, @@ -2248,7 +2273,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Node::GenericParam(param) = hir.get(id) { has_bounds = !param.bounds.is_empty(); } - let sp = hir.span(id); + let sp = self.tcx.def_span(def_id); // `sp` only covers `T`, change it so that it covers // `T:` when appropriate let is_impl_trait = bound_kind.to_string().starts_with("impl "); @@ -2294,12 +2319,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .as_ref() .and_then(|(_, g, _)| g.params.first()) .and_then(|param| param.def_id.as_local()) - .map(|def_id| { - ( - hir.span(hir.local_def_id_to_hir_id(def_id)).shrink_to_lo(), - format!("{}, ", new_lt), - ) - }); + .map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt))); let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), @@ -2657,7 +2677,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { infer::LateBoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( " for lifetime parameter {}in trait containing associated type `{}`", br_string(br), - self.tcx.associated_item(def_id).ident + self.tcx.associated_item(def_id).name ), infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name), infer::UpvarRegion(ref upvar_id, _) => { 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 9cf6cde259..205ad04455 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 @@ -4,15 +4,15 @@ 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::intravisit::{self, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, MatchSource, Pat}; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; 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, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder}; use rustc_span::symbol::kw; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::borrow::Cow; struct FindHirNodeVisitor<'a, 'tcx> { @@ -52,7 +52,7 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { 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(self.infcx.tcx).any(|inner| { + ty.walk().any(|inner| { inner == self.target || match (inner.unpack(), self.target.unpack()) { (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { @@ -83,10 +83,10 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.infcx.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.infcx.tcx.hir() } fn visit_local(&mut self, local: &'tcx Local<'tcx>) { @@ -121,8 +121,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { } } } - if let ExprKind::MethodCall(_, call_span, exprs, _) = expr.kind { - if call_span == self.target_span + if let ExprKind::MethodCall(segment, exprs, _) = expr.kind { + if segment.ident.span == self.target_span && Some(self.target) == self.infcx.in_progress_typeck_results.and_then(|typeck_results| { typeck_results @@ -369,7 +369,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn extract_inference_diagnostics_data( &self, arg: GenericArg<'tcx>, - highlight: Option, + highlight: Option>, ) -> InferenceDiagnosticsData { match arg.unpack() { GenericArgKind::Type(ty) => { @@ -409,7 +409,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } GenericArgKind::Const(ct) => { - match ct.val { + match ct.val() { ty::ConstKind::Infer(InferConst::Var(vid)) => { let origin = self .inner @@ -445,9 +445,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { parent: None, } } - ty::ConstKind::Unevaluated(ty::Unevaluated { - substs_: Some(substs), .. - }) => { + ty::ConstKind::Unevaluated(ty::Unevaluated { substs, .. }) => { assert!(substs.has_infer_types_or_consts()); // FIXME: We only use the first inference variable we encounter in @@ -461,7 +459,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } _ => {} }, - GenericArgKind::Const(c) => match c.val { + GenericArgKind::Const(c) => match c.val() { ty::ConstKind::Infer(InferConst::Var(_)) => { return self.extract_inference_diagnostics_data(s, None); } @@ -499,16 +497,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty_to_string = |ty: Ty<'tcx>| -> String { let mut s = String::new(); let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS); - let mut inner = self.inner.borrow_mut(); - let ty_vars = inner.type_variables(); - let getter = move |ty_vid| { - let var_origin = ty_vars.var_origin(ty_vid); - if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind { + let ty_getter = move |ty_vid| { + if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = + self.inner.borrow_mut().type_variables().var_origin(ty_vid).kind + { + Some(name.to_string()) + } else { + None + } + }; + printer.ty_infer_name_resolver = Some(Box::new(ty_getter)); + let const_getter = move |ct_vid| { + if let ConstVariableOriginKind::ConstParameterDefinition(name, _) = self + .inner + .borrow_mut() + .const_unification_table() + .probe_value(ct_vid) + .origin + .kind + { return Some(name.to_string()); + } else { + None } - None }; - printer.name_resolver = Some(Box::new(&getter)); + printer.const_infer_name_resolver = Some(Box::new(const_getter)); + let _ = if let ty::FnDef(..) = ty.kind() { // We don't want the regular output for `fn`s because it includes its path in // invalid pseudo-syntax, we want the `fn`-pointer output instead. @@ -533,18 +547,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // 3 | let _ = x.sum() as f64; // | ^^^ cannot infer type for `S` span - } else if let Some(ExprKind::MethodCall(_, call_span, _, _)) = + } else if let Some(ExprKind::MethodCall(segment, ..)) = local_visitor.found_method_call.map(|e| &e.kind) { // Point at the call instead of the whole expression: // error[E0284]: type annotations needed // --> file.rs:2:5 // | - // 2 | vec![Ok(2)].into_iter().collect()?; - // | ^^^^^^^ cannot infer type + // 2 | [Ok(2)].into_iter().collect()?; + // | ^^^^^^^ cannot infer type // | // = note: cannot resolve `<_ as std::ops::Try>::Ok == _` - if span.contains(*call_span) { *call_span } else { span } + if span.contains(segment.ident.span) { segment.ident.span } else { span } } else { span }; @@ -555,8 +569,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty_msg = match (local_visitor.found_node_ty, local_visitor.found_exact_method_call) { (_, Some(_)) => String::new(), (Some(ty), _) if ty.is_closure() => { - let substs = - if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() }; + let ty::Closure(_, substs) = *ty.kind() else { unreachable!() }; let fn_sig = substs.as_closure().sig(); let args = closure_args(&fn_sig); let ret = fn_sig.output().skip_binder().to_string(); @@ -599,8 +612,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let param_type = arg_data.kind.descr(); let suffix = match local_visitor.found_node_ty { Some(ty) if ty.is_closure() => { - let substs = - if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() }; + let ty::Closure(_, substs) = *ty.kind() else { unreachable!() }; let fn_sig = substs.as_closure().sig(); let ret = fn_sig.output().skip_binder().to_string(); @@ -711,7 +723,7 @@ 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, _, exprs, _) = &e.kind { + if let ExprKind::MethodCall(segment, exprs, _) = &e.kind { // Suggest impl candidates: // // error[E0283]: type annotations needed @@ -937,9 +949,9 @@ impl<'tcx> ResolvedTypeParamEraser<'tcx> { } /// Replace not yet inferred const params with their def name. - fn replace_infers(&self, c: &'tcx Const<'tcx>, index: u32, name: Symbol) -> &'tcx Const<'tcx> { - match c.val { - ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty), + fn replace_infers(&self, c: Const<'tcx>, index: u32, name: Symbol) -> Const<'tcx> { + match c.val() { + ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty()), _ => c, } } @@ -964,7 +976,7 @@ impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> { .map(|(subst, param)| match &(subst.unpack(), ¶m.kind) { (_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst, (crate::infer::GenericArgKind::Const(c), _) => { - self.replace_infers(c, param.index, param.name).into() + self.replace_infers(*c, param.index, param.name).into() } _ => subst.super_fold_with(self), }) @@ -987,7 +999,7 @@ impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> { } } ty::Ref(_, ty, _) => { - let ty = self.fold_ty(ty); + let ty = self.fold_ty(*ty); match ty.kind() { // Avoid `&_`, these can be safely presented as `_`. ty::Error(_) => self.tcx().ty_error(), @@ -1003,9 +1015,9 @@ impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> { | ty::Opaque(..) | ty::Projection(_) | ty::Never => t.super_fold_with(self), - ty::Array(ty, c) => self - .tcx() - .mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, Symbol::intern("N")))), + ty::Array(ty, c) => { + self.tcx().mk_ty(ty::Array(self.fold_ty(*ty), self.replace_infers(*c, 0, sym::N))) + } // We don't want to hide type params that haven't been resolved yet. // This would be the type that will be written out with the type param // name in the output. diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index ac57796763..4eec492b3a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -106,90 +106,47 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { None => String::new(), }; - let (span_1, span_2, main_label, span_label, future_return_type) = - match (sup_is_ret_type, sub_is_ret_type) { - (None, None) => { - let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id { - ( - "this type is declared with multiple lifetimes...".to_owned(), - "...but data with one lifetime flows into the other here".to_owned(), - ) - } else { - ( - "these two types are declared with different lifetimes...".to_owned(), - format!("...but data{} flows{} here", span_label_var1, span_label_var2), - ) - }; - (ty_sup.span, ty_sub.span, main_label_1, span_label_1, None) - } + debug!( + "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", + sub_is_ret_type, sup_is_ret_type + ); - (Some(ret_span), _) => { - let sup_future = self.future_return_type(scope_def_id_sup); - let (return_type, action) = if sup_future.is_some() { - ("returned future", "held across an await point") - } else { - ("return type", "returned") - }; + let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); - ( - ty_sub.span, - ret_span, - format!( - "this parameter and the {} are declared with different lifetimes...", - return_type - ), - format!("...but data{} is {} here", span_label_var1, action), - sup_future, - ) - } - (_, Some(ret_span)) => { - let sub_future = self.future_return_type(scope_def_id_sub); - let (return_type, action) = if sub_future.is_some() { - ("returned future", "held across an await point") - } else { - ("return type", "returned") - }; + match (sup_is_ret_type, sub_is_ret_type) { + (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => { + let param_span = + if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span }; + + err.span_label( + param_span, + "this parameter and the return type are declared with different lifetimes...", + ); + err.span_label(ret_span, ""); + err.span_label(span, format!("...but data{} is returned here", span_label_var1)); + } - ( + (None, None) => { + if ty_sup.hir_id == ty_sub.hir_id { + err.span_label(ty_sup.span, "this type is declared with multiple lifetimes..."); + err.span_label(ty_sub.span, ""); + err.span_label(span, "...but data with one lifetime flows into the other here"); + } else { + err.span_label( ty_sup.span, - ret_span, - format!( - "this parameter and the {} are declared with different lifetimes...", - return_type - ), - format!("...but data{} is {} here", span_label_var1, action), - sub_future, - ) + "these two types are declared with different lifetimes...", + ); + err.span_label(ty_sub.span, ""); + err.span_label( + span, + format!("...but data{} flows{} here", span_label_var1, span_label_var2), + ); } - }; - - let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); - - err.span_label(span_1, main_label); - err.span_label(span_2, String::new()); - err.span_label(span, span_label); + } + } self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err); - if let Some(t) = future_return_type { - let snip = self - .tcx() - .sess - .source_map() - .span_to_snippet(t.span) - .ok() - .and_then(|s| match (&t.kind, s.as_str()) { - (rustc_hir::TyKind::Tup(&[]), "") => Some("()".to_string()), - (_, "") => None, - _ => Some(s), - }) - .unwrap_or_else(|| "{unnamed_type}".to_string()); - - err.span_label( - t.span, - &format!("this `async fn` implicitly returns an `impl Future`", snip), - ); - } err.emit(); Some(ErrorReported) } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index 89023101f3..b1535701bb 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -1,7 +1,7 @@ use rustc_hir as hir; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::Node; +use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime as rl; use rustc_middle::ty::{self, Region, TyCtxt}; @@ -24,25 +24,19 @@ pub(crate) fn find_anon_type<'tcx>( tcx: TyCtxt<'tcx>, region: Region<'tcx>, br: &ty::BoundRegionKind, -) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnDecl<'tcx>)> { +) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> { if let Some(anon_reg) = tcx.is_suitable_region(region) { let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); - let fndecl = match tcx.hir().get(hir_id) { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref m, ..), .. }) - | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Fn(ref m, ..), .. - }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref m, ..), .. }) => { - &m.decl - } - _ => return None, + let Some(fn_sig) = tcx.hir().get(hir_id).fn_sig() else { + return None }; - fndecl + fn_sig + .decl .inputs .iter() .find_map(|arg| find_component_for_bound_region(tcx, arg, br)) - .map(|ty| (ty, &**fndecl)) + .map(|ty| (ty, fn_sig)) } else { None } @@ -84,10 +78,10 @@ struct FindNestedTypeVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { @@ -208,10 +202,10 @@ struct TyPathVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap> { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Map<'tcx> { + self.tcx.hir() } fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index d3b47e396e..ef4c9c24f3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -10,7 +10,7 @@ use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::{Applicability, ErrorReported}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; -use rustc_middle::ty::{self, TypeVisitor}; +use rustc_middle::ty::TypeVisitor; use rustc_span::MultiSpan; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -22,7 +22,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { RegionResolutionError::ConcreteFailure(origin, sub, sup) => (origin, sub, sup), _ => return None, }; - if *sub != ty::RegionKind::ReStatic { + if !sub.is_static() { return None; } let cause = match origin { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index fd295b7434..f44e6e0434 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -64,11 +64,11 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { .or_else(|| self.try_report_mismatched_static_lifetime()) } - pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { + pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { match (&self.error, self.regions) { - (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), sub, sup)), + (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)), (Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => { - Some((origin.span(), sub, sup)) + Some((origin.span(), *sub, *sup)) } (None, Some((span, sub, sup))) => Some((span, sub, sup)), _ => None, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs index eb1c80ecb0..17ff5d45c8 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -48,7 +48,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect, // and can steer users down the wrong path. - if *named == ty::ReStatic { + if named.is_static() { return None; } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index 7178bfa525..7d82c60e6d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -3,13 +3,14 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::ValuePairs; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCause, ObligationCauseCode}; +use rustc_data_structures::intern::Interned; use rustc_errors::DiagnosticBuilder; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode}; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, RePlaceholder, ReVar, Region, TyCtxt}; use std::fmt::{self, Write}; @@ -31,15 +32,15 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { vid, _, SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_placeholder @ ty::RePlaceholder(_), + sub_placeholder @ Region(Interned(RePlaceholder(_), _)), _, - sup_placeholder @ ty::RePlaceholder(_), + sup_placeholder @ Region(Interned(RePlaceholder(_), _)), _, )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ty::ReVar(*vid))), + Some(self.tcx().mk_region(ReVar(*vid))), cause, - Some(sub_placeholder), - Some(sup_placeholder), + Some(*sub_placeholder), + Some(*sup_placeholder), values, ), @@ -47,14 +48,14 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { vid, _, SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_placeholder @ ty::RePlaceholder(_), + sub_placeholder @ Region(Interned(RePlaceholder(_), _)), _, _, _, )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ty::ReVar(*vid))), + Some(self.tcx().mk_region(ReVar(*vid))), cause, - Some(sub_placeholder), + Some(*sub_placeholder), None, values, ), @@ -65,10 +66,10 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { SubregionOrigin::Subtype(box TypeTrace { cause, values }), _, _, - sup_placeholder @ ty::RePlaceholder(_), + sup_placeholder @ Region(Interned(RePlaceholder(_), _)), _, )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ty::ReVar(*vid))), + Some(self.tcx().mk_region(ReVar(*vid))), cause, None, Some(*sup_placeholder), @@ -81,10 +82,10 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { _, _, SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sup_placeholder @ ty::RePlaceholder(_), + sup_placeholder @ Region(Interned(RePlaceholder(_), _)), _, )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ty::ReVar(*vid))), + Some(self.tcx().mk_region(ReVar(*vid))), cause, None, Some(*sup_placeholder), @@ -96,9 +97,9 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { _, _, SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sup_placeholder @ ty::RePlaceholder(_), + sup_placeholder @ Region(Interned(RePlaceholder(_), _)), )) => self.try_report_trait_placeholder_mismatch( - Some(self.tcx().mk_region(ty::ReVar(*vid))), + Some(self.tcx().mk_region(ReVar(*vid))), cause, None, Some(*sup_placeholder), @@ -107,8 +108,8 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { Some(RegionResolutionError::ConcreteFailure( SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_region @ ty::RePlaceholder(_), - sup_region @ ty::RePlaceholder(_), + sub_region @ Region(Interned(RePlaceholder(_), _)), + sup_region @ Region(Interned(RePlaceholder(_), _)), )) => self.try_report_trait_placeholder_mismatch( None, cause, @@ -119,12 +120,12 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { Some(RegionResolutionError::ConcreteFailure( SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_region @ ty::RePlaceholder(_), + sub_region @ Region(Interned(RePlaceholder(_), _)), sup_region, )) => self.try_report_trait_placeholder_mismatch( - (!sup_region.has_name()).then_some(sup_region), + (!sup_region.has_name()).then_some(*sup_region), cause, - Some(sub_region), + Some(*sub_region), None, values, ), @@ -132,12 +133,12 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { Some(RegionResolutionError::ConcreteFailure( SubregionOrigin::Subtype(box TypeTrace { cause, values }), sub_region, - sup_region @ ty::RePlaceholder(_), + sup_region @ Region(Interned(RePlaceholder(_), _)), )) => self.try_report_trait_placeholder_mismatch( - (!sub_region.has_name()).then_some(sub_region), + (!sub_region.has_name()).then_some(*sub_region), cause, None, - Some(sup_region), + Some(*sup_region), values, ), @@ -147,10 +148,10 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { fn try_report_trait_placeholder_mismatch( &self, - vid: Option>, + vid: Option>, cause: &ObligationCause<'tcx>, - sub_placeholder: Option>, - sup_placeholder: Option>, + sub_placeholder: Option>, + sup_placeholder: Option>, value_pairs: &ValuePairs<'tcx>, ) -> Option> { let (expected_substs, found_substs, trait_def_id) = match value_pairs { @@ -193,10 +194,10 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { #[instrument(level = "debug", skip(self))] fn report_trait_placeholder_mismatch( &self, - vid: Option>, + vid: Option>, cause: &ObligationCause<'tcx>, - sub_placeholder: Option>, - sup_placeholder: Option>, + sub_placeholder: Option>, + sup_placeholder: Option>, trait_def_id: DefId, expected_substs: SubstsRef<'tcx>, actual_substs: SubstsRef<'tcx>, @@ -306,13 +307,13 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { fn explain_actual_impl_that_was_found( &self, err: &mut DiagnosticBuilder<'_>, - sub_placeholder: Option>, - sup_placeholder: Option>, + sub_placeholder: Option>, + sup_placeholder: Option>, has_sub: Option, has_sup: Option, expected_trait_ref: ty::TraitRef<'tcx>, actual_trait_ref: ty::TraitRef<'tcx>, - vid: Option>, + vid: Option>, expected_has_vid: Option, actual_has_vid: Option, any_self_ty_has_vid: bool, @@ -322,7 +323,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { #[derive(Copy, Clone)] struct Highlighted<'tcx, T> { tcx: TyCtxt<'tcx>, - highlight: RegionHighlightMode, + highlight: RegionHighlightMode<'tcx>, value: T, } @@ -366,7 +367,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { let highlight_trait_ref = |trait_ref| Highlighted { tcx: self.tcx(), - highlight: RegionHighlightMode::default(), + highlight: RegionHighlightMode::new(self.tcx()), value: trait_ref, }; 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 b6dff2e53e..625fd86421 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 @@ -7,11 +7,10 @@ use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; use rustc_middle::ty::{ - self, AssocItemContainer, RegionKind, StaticLifetimeVisitor, Ty, TyCtxt, TypeFoldable, - TypeVisitor, + self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeFoldable, TypeVisitor, }; use rustc_span::symbol::Ident; use rustc_span::{MultiSpan, Span}; @@ -33,25 +32,23 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { sup_origin, sup_r, spans, - ) if **sub_r == RegionKind::ReStatic => { - (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) - } + ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans), RegionResolutionError::ConcreteFailure( SubregionOrigin::Subtype(box TypeTrace { cause, .. }), sub_r, sup_r, - ) if **sub_r == RegionKind::ReStatic => { + ) if sub_r.is_static() => { // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`. if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() { // This may have a closure and it would cause ICE // through `find_param_with_region` (#78262). - let anon_reg_sup = tcx.is_suitable_region(sup_r)?; + let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); if fn_returns.is_empty() { return None; } - let param = self.find_param_with_region(sup_r, sub_r)?; + let param = self.find_param_with_region(*sup_r, *sub_r)?; let lifetime = if sup_r.has_name() { format!("lifetime `{}`", sup_r) } else { @@ -70,7 +67,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .map(|s| format!("`{}`", s)) .unwrap_or_else(|| "`fn` parameter".to_string()), lifetime, - ctxt.assoc_item.ident, + ctxt.assoc_item.name, ); err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); err.span_label( @@ -101,11 +98,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})", var_origin, sub_origin, sub_r, sup_origin, sup_r ); - let anon_reg_sup = tcx.is_suitable_region(sup_r)?; + let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup); let sp = var_origin.span(); let return_sp = sub_origin.span(); - let param = self.find_param_with_region(sup_r, sub_r)?; + let param = self.find_param_with_region(*sup_r, *sub_r)?; let (lifetime_name, lifetime) = if sup_r.has_name() { (sup_r.to_string(), format!("lifetime `{}`", sup_r)) } else { @@ -187,6 +184,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code() { let parent_id = tcx.hir().get_parent_item(*hir_id); + let parent_id = tcx.hir().local_def_id_to_hir_id(parent_id); if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id) { let mut span: MultiSpan = fn_decl.output.span().into(); let mut add_label = true; @@ -230,7 +228,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a // `'static` lifetime when called as a method on a binding: `bar.qux()`. if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) { - override_error_code = Some(ctxt.assoc_item.ident); + override_error_code = Some(ctxt.assoc_item.name); } } } @@ -251,7 +249,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { self.get_impl_ident_and_self_ty_from_trait(*item_def_id, &v.0) { if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) { - override_error_code = Some(ident); + override_error_code = Some(ident.name); } } } @@ -425,7 +423,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let tcx = self.tcx(); match tcx.hir().get_if_local(def_id) { Some(Node::ImplItem(impl_item)) => { - match tcx.hir().find(tcx.hir().get_parent_item(impl_item.hir_id())) { + match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id())) { Some(Node::Item(Item { kind: ItemKind::Impl(hir::Impl { self_ty, .. }), .. @@ -434,13 +432,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } Some(Node::TraitItem(trait_item)) => { - let parent_id = tcx.hir().get_parent_item(trait_item.hir_id()); - match tcx.hir().find(parent_id) { + let trait_did = tcx.hir().get_parent_item(trait_item.hir_id()); + match tcx.hir().find_by_def_id(trait_did) { Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => { // The method being called is defined in the `trait`, but the `'static` // obligation comes from the `impl`. Find that `impl` so that we can point // at it in the suggestion. - let trait_did = tcx.hir().local_def_id(parent_id).to_def_id(); + let trait_did = trait_did.to_def_id(); match tcx .hir() .trait_impls(trait_did) @@ -557,15 +555,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) struct TraitObjectVisitor(pub(super) FxHashSet); impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { - fn tcx_for_anon_const_substs(&self) -> Option> { - // The default anon const substs cannot include - // trait objects, so we don't have to bother looking. - None - } - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match t.kind() { - ty::Dynamic(preds, RegionKind::ReStatic) => { + ty::Dynamic(preds, re) if re.is_static() => { if let Some(def_id) = preds.principal_def_id() { self.0.insert(def_id); } @@ -580,12 +572,6 @@ impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { pub(super) struct HirTraitObjectVisitor<'a>(pub(super) &'a mut Vec, pub(super) DefId); impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { - type Map = ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { if let TyKind::TraitObject( poly_trait_refs, 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 f5fb82dbf3..9216fa3ca1 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 @@ -2,13 +2,14 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; -use crate::infer::{SubregionOrigin, Subtype, ValuePairs}; +use crate::infer::{SubregionOrigin, Subtype}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; @@ -33,16 +34,16 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { { if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin) { if let ( - ValuePairs::Types(sub_expected_found), - ValuePairs::Types(sup_expected_found), + sub_expected_found @ Some((sub_expected, sub_found)), + sup_expected_found @ Some(_), CompareImplMethodObligation { trait_item_def_id, .. }, - ) = (&sub_trace.values, &sup_trace.values, sub_trace.cause.code()) + ) = (&sub_trace.values.ty(), &sup_trace.values.ty(), sub_trace.cause.code()) { if sup_expected_found == sub_expected_found { self.emit_err( var_origin.span(), - sub_expected_found.expected, - sub_expected_found.found, + *sub_expected, + *sub_found, *trait_item_def_id, ); return Some(ErrorReported); @@ -81,25 +82,20 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // Mark all unnamed regions in the type with a number. // This diagnostic is called in response to lifetime errors, so be informative. struct HighlightBuilder<'tcx> { - highlight: RegionHighlightMode, - tcx: TyCtxt<'tcx>, + highlight: RegionHighlightMode<'tcx>, counter: usize, } impl<'tcx> HighlightBuilder<'tcx> { - fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode { + fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> { let mut builder = - HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1, tcx }; + HighlightBuilder { highlight: RegionHighlightMode::new(tcx), counter: 1 }; builder.visit_ty(ty); builder.highlight } } impl<'tcx> ty::fold::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { if !r.has_name() && self.counter <= 3 { self.highlight.highlighting_region(r, self.counter); @@ -187,10 +183,10 @@ struct TypeParamSpanVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { - type Map = rustc_middle::hir::map::Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { @@ -207,7 +203,8 @@ impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { .map(|res| { matches!( res, - Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _) + Res::SelfTy { trait_: _, alias_to: _ } + | Res::Def(hir::def::DefKind::TyParam, _) ) }) .unwrap_or(false) => diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 04eceecc5f..719f6b37a4 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -4,7 +4,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::{self, DefIdTree, Region, Ty}; +use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeFoldable}; use rustc_span::Span; /// Information about the anonymous region we are searching for. @@ -70,7 +70,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let ty = fn_sig.inputs()[index]; let mut found_anon_region = false; let new_param_ty = self.tcx().fold_regions(ty, &mut false, |r, _| { - if *r == *anon_region { + if r == anon_region { found_anon_region = true; replace_region } else { @@ -94,80 +94,42 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { }) } - pub(super) fn future_return_type( - &self, - local_def_id: LocalDefId, - ) -> Option<&rustc_hir::Ty<'_>> { - if let Some(hir::IsAsync::Async) = self.asyncness(local_def_id) { - if let rustc_middle::ty::Opaque(def_id, _) = - self.tcx().type_of(local_def_id).fn_sig(self.tcx()).output().skip_binder().kind() - { - match self.tcx().hir().get_if_local(*def_id) { - Some(hir::Node::Item(hir::Item { - kind: - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - bounds, - origin: hir::OpaqueTyOrigin::AsyncFn(..), - .. - }), - .. - })) => { - for b in bounds.iter() { - if let hir::GenericBound::LangItemTrait( - hir::LangItem::Future, - _span, - _hir_id, - generic_args, - ) = b - { - for type_binding in generic_args.bindings.iter() { - if type_binding.ident.name == rustc_span::sym::Output { - if let hir::TypeBindingKind::Equality { ty } = - type_binding.kind - { - return Some(ty); - } - } - } - } - } - } - _ => {} - } - } - } - None - } - - pub(super) fn asyncness(&self, local_def_id: LocalDefId) -> Option { - // similar to the asyncness fn in rustc_ty_utils::ty - let hir_id = self.tcx().hir().local_def_id_to_hir_id(local_def_id); - let node = self.tcx().hir().get(hir_id); - let fn_kind = node.fn_kind()?; - Some(fn_kind.asyncness()) - } - // Here, we check for the case where the anonymous region - // is in the return type. + // is in the return type as written by the user. // FIXME(#42703) - Need to handle certain cases here. pub(super) fn is_return_type_anon( &self, scope_def_id: LocalDefId, br: ty::BoundRegionKind, - decl: &hir::FnDecl<'_>, + hir_sig: &hir::FnSig<'_>, ) -> Option { - let ret_ty = self.tcx().type_of(scope_def_id); - if let ty::FnDef(_, _) = ret_ty.kind() { - let sig = ret_ty.fn_sig(self.tcx()); - let late_bound_regions = - self.tcx().collect_referenced_late_bound_regions(&sig.output()); - if late_bound_regions.iter().any(|r| *r == br) { - return Some(decl.output.span()); - } + let fn_ty = self.tcx().type_of(scope_def_id); + if let ty::FnDef(_, _) = fn_ty.kind() { + let ret_ty = fn_ty.fn_sig(self.tcx()).output(); + let span = hir_sig.decl.output.span(); + let future_output = if hir_sig.header.is_async() { + ret_ty.map_bound(|ty| self.infcx.get_impl_future_output_ty(ty)).transpose() + } else { + None + }; + return match future_output { + Some(output) if self.includes_region(output, br) => Some(span), + None if self.includes_region(ret_ty, br) => Some(span), + _ => None, + }; } None } + fn includes_region( + &self, + ty: Binder<'tcx, impl TypeFoldable<'tcx>>, + region: ty::BoundRegionKind, + ) -> bool { + let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty); + late_bound_regions.iter().any(|r| *r == region) + } + // Here we check for the case where anonymous region // corresponds to self and if yes, we display E0312. // FIXME(#42700) - Need to format self properly to diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 82bd8890dd..8671ecba6e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -102,6 +102,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "...so that the definition in impl matches the definition from the trait", ); } + infer::CheckAssociatedTypeBounds { ref parent, .. } => { + self.note_region_origin(err, &parent); + } } } @@ -115,7 +118,7 @@ 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); - match (sub, sup) { + match (*sub, *sup) { (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} (ty::RePlaceholder(_), _) => { note_and_explain_region( @@ -345,6 +348,55 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { trait_item_def_id, &format!("`{}: {}`", sup, sub), ), + infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { + let mut err = self.report_concrete_failure(*parent, sub, sup); + + let trait_item_span = self.tcx.def_span(trait_item_def_id); + let item_name = self.tcx.item_name(impl_item_def_id); + err.span_label( + trait_item_span, + format!("definition of `{}` from trait", item_name), + ); + + let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id); + let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id); + + let impl_predicates: rustc_data_structures::stable_set::FxHashSet<_> = + impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect(); + let clauses: Vec<_> = trait_predicates + .predicates + .into_iter() + .filter(|&(pred, _)| !impl_predicates.contains(pred)) + .map(|(pred, _)| format!("{}", pred)) + .collect(); + + if !clauses.is_empty() { + let where_clause_span = self + .tcx + .hir() + .get_generics(impl_item_def_id.expect_local()) + .unwrap() + .where_clause + .tail_span_for_suggestion(); + + let suggestion = format!( + "{} {}", + if !impl_predicates.is_empty() { "," } else { " where" }, + clauses.join(", "), + ); + err.span_suggestion( + where_clause_span, + &format!( + "try copying {} from the trait", + if clauses.len() > 1 { "these clauses" } else { "this clause" } + ), + suggestion, + rustc_errors::Applicability::MaybeIncorrect, + ); + } + + err + } } } diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index e93cdf7942..187c67df3e 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -41,8 +41,8 @@ pub struct FreeRegionMap<'tcx> { } impl<'tcx> FreeRegionMap<'tcx> { - pub fn elements(&self) -> impl Iterator> { - self.relation.elements() + pub fn elements(&self) -> impl Iterator> + '_ { + self.relation.elements().copied() } pub fn is_empty(&self) -> bool { @@ -91,7 +91,7 @@ impl<'tcx> FreeRegionMap<'tcx> { /// True for free regions other than `'static`. pub fn is_free(&self, r: Region<'_>) -> bool { - matches!(r, ty::ReEarlyBound(_) | ty::ReFree(_)) + 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/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index c40e409891..e9d3b6a8aa 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -46,7 +46,7 @@ pub struct TypeFreshener<'a, 'tcx> { ty_freshen_count: u32, const_freshen_count: u32, ty_freshen_map: FxHashMap>, - const_freshen_map: FxHashMap, &'tcx ty::Const<'tcx>>, + const_freshen_map: FxHashMap, ty::Const<'tcx>>, keep_static: bool, } @@ -89,11 +89,11 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { fn freshen_const( &mut self, - opt_ct: Option<&'tcx ty::Const<'tcx>>, + opt_ct: Option>, key: ty::InferConst<'tcx>, freshener: F, ty: Ty<'tcx>, - ) -> &'tcx ty::Const<'tcx> + ) -> ty::Const<'tcx> where F: FnOnce(u32) -> ty::InferConst<'tcx>, { @@ -146,7 +146,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.needs_infer() && !t.has_erasable_regions(self.tcx()) { + if !t.needs_infer() && !t.has_erasable_regions() { return t; } @@ -221,8 +221,8 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { } } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - match ct.val { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.val() { ty::ConstKind::Infer(ty::InferConst::Var(v)) => { let opt_ct = self .infcx @@ -236,7 +236,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, - ct.ty, + ct.ty(), ); } ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => { diff --git a/compiler/rustc_infer/src/infer/fudge.rs b/compiler/rustc_infer/src/infer/fudge.rs index 773753a036..c5c131a5b7 100644 --- a/compiler/rustc_infer/src/infer/fudge.rs +++ b/compiler/rustc_infer/src/infer/fudge.rs @@ -230,14 +230,14 @@ impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> { r } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { val: ty::ConstKind::Infer(ty::InferConst::Var(vid)), ty } = ct { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.val() { if self.const_vars.0.contains(&vid) { // This variable was created during the fudging. // Recreate it with a fresh variable here. let idx = (vid.index - self.const_vars.0.start.index) as usize; let origin = self.const_vars.1[idx]; - self.infcx.next_const_var(ty, origin) + self.infcx.next_const_var(ct.ty(), origin) } else { ct } diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs index 862f5a5fbb..381097344e 100644 --- a/compiler/rustc_infer/src/infer/glb.rs +++ b/compiler/rustc_infer/src/infer/glb.rs @@ -78,9 +78,9 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { self.fields.infcx.super_combine_consts(self, a, b) } @@ -120,7 +120,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, } impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { self.fields.add_const_equate_obligation(self.a_is_expected, a, b); } } diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index ae85e55da6..82454b8915 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -94,7 +94,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let fld_c = |bound_var: ty::BoundVar, ty| { - self.tcx.mk_const(ty::Const { + self.tcx.mk_const(ty::ConstS { val: ty::ConstKind::Placeholder(ty::PlaceholderConst { universe: next_universe, name: ty::BoundConst { var: bound_var, ty }, 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 a5ec84a4f1..4e50585ff5 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -13,6 +13,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::implementation::{ Direction, Graph, NodeIndex, INCOMING, OUTGOING, }; +use rustc_data_structures::intern::Interned; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -61,6 +62,7 @@ pub(crate) fn resolve<'tcx>( /// Contains the result of lexical region resolution. Offers methods /// to lookup up the final value of a region variable. +#[derive(Clone)] pub struct LexicalRegionResolutions<'tcx> { values: IndexVec>, error_region: ty::Region<'tcx>, @@ -249,8 +251,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { changes.push(b_vid); } if let Some(a_vid) = a_vid { - match *b_data { - VarValue::Value(ReStatic) | VarValue::ErrorValue => (), + match b_data { + VarValue::Value(Region(Interned(ReStatic, _))) | VarValue::ErrorValue => (), _ => { constraints[a_vid].push((a_vid, b_vid)); constraints[b_vid].push((a_vid, b_vid)); @@ -269,7 +271,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { if self.expand_node(a_region, b_vid, b_data) { changes.push(b_vid); } - !matches!(b_data, VarValue::Value(ReStatic) | VarValue::ErrorValue) + !matches!( + b_data, + VarValue::Value(Region(Interned(ReStatic, _))) | VarValue::ErrorValue + ) }); } } @@ -300,8 +305,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // check below for a common case, here purely as an // optimization. let b_universe = self.var_infos[b_vid].universe; - if let ReEmpty(a_universe) = a_region { - if *a_universe == b_universe { + if let ReEmpty(a_universe) = *a_region { + if a_universe == b_universe { return false; } } @@ -320,7 +325,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // tighter bound than `'static`. // // (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.) - if let ty::RePlaceholder(p) = lub { + if let ty::RePlaceholder(p) = *lub { if b_universe.cannot_name(p.universe) { lub = self.tcx().lifetimes.re_static; } @@ -371,12 +376,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// term "concrete regions"). #[instrument(level = "trace", skip(self))] fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { - let r = match (a, b) { - (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { + let r = match (*a, *b) { + (ReLateBound(..), _) | (_, ReLateBound(..)) | (ReErased, _) | (_, ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } - (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { + (ReVar(v_id), _) | (_, ReVar(v_id)) => { span_bug!( self.var_infos[v_id].origin.span(), "lub_concrete_regions invoked with non-concrete \ @@ -386,27 +391,32 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); } - (&ReStatic, _) | (_, &ReStatic) => { + (ReStatic, _) | (_, ReStatic) => { // nothing lives longer than `'static` self.tcx().lifetimes.re_static } - (&ReEmpty(_), r @ (ReEarlyBound(_) | ReFree(_))) - | (r @ (ReEarlyBound(_) | ReFree(_)), &ReEmpty(_)) => { + (ReEmpty(_), ReEarlyBound(_) | ReFree(_)) => { // All empty regions are less than early-bound, free, // and scope regions. - r + b } - (&ReEmpty(a_ui), &ReEmpty(b_ui)) => { + (ReEarlyBound(_) | ReFree(_), ReEmpty(_)) => { + // All empty regions are less than early-bound, free, + // and scope regions. + a + } + + (ReEmpty(a_ui), ReEmpty(b_ui)) => { // Empty regions are ordered according to the universe // they are associated with. let ui = a_ui.min(b_ui); self.tcx().mk_region(ReEmpty(ui)) } - (&ReEmpty(empty_ui), &RePlaceholder(placeholder)) - | (&RePlaceholder(placeholder), &ReEmpty(empty_ui)) => { + (ReEmpty(empty_ui), RePlaceholder(placeholder)) + | (RePlaceholder(placeholder), ReEmpty(empty_ui)) => { // If this empty region is from a universe that can // name the placeholder, then the placeholder is // larger; otherwise, the only ancestor is `'static`. @@ -417,13 +427,13 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - (&ReEarlyBound(_) | &ReFree(_), &ReEarlyBound(_) | &ReFree(_)) => { + (ReEarlyBound(_) | ReFree(_), ReEarlyBound(_) | ReFree(_)) => { self.region_rels.lub_free_regions(a, b) } // For these types, we cannot define any additional // relationship: - (&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => { + (RePlaceholder(..), _) | (_, RePlaceholder(..)) => { if a == b { a } else { @@ -675,7 +685,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let node_universe = self.var_infos[node_idx].universe; for lower_bound in &lower_bounds { - let effective_lower_bound = if let ty::RePlaceholder(p) = lower_bound.region { + let effective_lower_bound = if let ty::RePlaceholder(p) = *lower_bound.region { if node_universe.cannot_name(p.universe) { self.tcx().lifetimes.re_static } else { @@ -720,7 +730,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { .expect("lower_vid_bounds should at least include `node_idx`"); for upper_bound in &upper_bounds { - if let ty::RePlaceholder(p) = upper_bound.region { + if let ty::RePlaceholder(p) = *upper_bound.region { if min_universe.cannot_name(p.universe) { let origin = self.var_infos[node_idx].origin; errors.push(RegionResolutionError::UpperBoundUniverseConflict( @@ -854,11 +864,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } VerifyBound::OutlivedBy(r) => { - self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r)) + self.sub_concrete_regions(min, var_values.normalize(self.tcx(), *r)) } VerifyBound::IsEmpty => { - matches!(min, ty::ReEmpty(_)) + matches!(*min, ty::ReEmpty(_)) } VerifyBound::AnyBound(bs) => { @@ -883,8 +893,8 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { where T: TypeFoldable<'tcx>, { - tcx.fold_regions(value, &mut false, |r, _db| match r { - ty::ReVar(rid) => self.resolve_var(*rid), + tcx.fold_regions(value, &mut false, |r, _db| match *r { + ty::ReVar(rid) => self.resolve_var(rid), _ => r, }) } diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs index 5191d1c1cc..57cbe2c54f 100644 --- a/compiler/rustc_infer/src/infer/lub.rs +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -78,9 +78,9 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { self.fields.infcx.super_combine_consts(self, a, b) } @@ -103,7 +103,7 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { } impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { self.fields.add_const_equate_obligation(self.a_is_expected, a, b); } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 04e04e297c..57ac98ca89 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -10,7 +10,6 @@ pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; -use hir::def_id::CRATE_DEF_ID; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; @@ -131,6 +130,7 @@ impl RegionckMode { /// `RefCell` and are involved with taking/rolling back snapshots. Snapshot /// operations are hot enough that we want only one call to `borrow_mut` per /// call to `start_snapshot` and `rollback_to`. +#[derive(Clone)] pub struct InferCtxtInner<'tcx> { /// Cache for projections. This cache is snapshotted along with the infcx. /// @@ -196,7 +196,7 @@ pub struct InferCtxtInner<'tcx> { // Opaque types found in explicit return types and their // associated fresh inference variable. Writeback resolves these // variables to get the concrete type, which can be used to - // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions. + // 'de-opaque' OpaqueTypeDecl outside of type inference. pub opaque_types: OpaqueTypeMap<'tcx>, /// A map from inference variables created from opaque @@ -291,7 +291,12 @@ pub struct InferCtxt<'a, 'tcx> { /// The `DefId` of the item in whose context we are performing inference or typeck. /// It is used to check whether an opaque type use is a defining use. - pub defining_use_anchor: LocalDefId, + /// + /// If it is `None`, we can't resolve opaque types here and need to bubble up + /// the obligation. This frequently happens for + /// short lived InferCtxt within queries. The opaque type obligations are forwarded + /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. + pub defining_use_anchor: Option, /// During type-checking/inference of a body, `in_progress_typeck_results` /// contains a reference to the typeck results being built up, which are @@ -364,13 +369,26 @@ pub struct InferCtxt<'a, 'tcx> { /// See the `error_reporting` module for more details. #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] pub enum ValuePairs<'tcx> { - Types(ExpectedFound>), Regions(ExpectedFound>), - Consts(ExpectedFound<&'tcx ty::Const<'tcx>>), + Terms(ExpectedFound>), TraitRefs(ExpectedFound>), PolyTraitRefs(ExpectedFound>), } +impl<'tcx> ValuePairs<'tcx> { + pub fn ty(&self) -> Option<(Ty<'tcx>, Ty<'tcx>)> { + if let ValuePairs::Terms(ExpectedFound { + expected: ty::Term::Ty(expected), + found: ty::Term::Ty(found), + }) = self + { + Some((*expected, *found)) + } else { + None + } + } +} + /// The trace designates the path through inference that we took to /// encounter an error or subtyping constraint. /// @@ -420,6 +438,13 @@ pub enum SubregionOrigin<'tcx> { /// Comparing the signature and requirements of an impl associated type /// against the containing trait CompareImplTypeObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId }, + + /// Checking that the bounds of a trait's associated type hold for a given impl + CheckAssociatedTypeBounds { + parent: Box>, + impl_item_def_id: DefId, + trait_item_def_id: DefId, + }, } // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. @@ -547,7 +572,7 @@ impl<'tcx> fmt::Display for FixupError<'tcx> { pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, fresh_typeck_results: Option>>, - defining_use_anchor: LocalDefId, + defining_use_anchor: Option, } pub trait TyCtxtInferExt<'tcx> { @@ -556,11 +581,7 @@ pub trait TyCtxtInferExt<'tcx> { impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { - InferCtxtBuilder { - tcx: self, - defining_use_anchor: CRATE_DEF_ID, - fresh_typeck_results: None, - } + InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None } } } @@ -580,7 +601,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used /// in mir borrowck. pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self { - self.defining_use_anchor = defining_use_anchor; + self.defining_use_anchor = Some(defining_use_anchor); self } @@ -1065,11 +1086,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.mk_ty_var(vid) } - pub fn next_const_var( - &self, - ty: Ty<'tcx>, - origin: ConstVariableOrigin, - ) -> &'tcx ty::Const<'tcx> { + pub fn next_const_var(&self, ty: Ty<'tcx>, origin: ConstVariableOrigin) -> ty::Const<'tcx> { self.tcx.mk_const_var(self.next_const_var_id(origin), ty) } @@ -1078,7 +1095,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ty: Ty<'tcx>, origin: ConstVariableOrigin, universe: ty::UniverseIndex, - ) -> &'tcx ty::Const<'tcx> { + ) -> ty::Const<'tcx> { let vid = self .inner .borrow_mut() @@ -1421,7 +1438,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn probe_const_var( &self, vid: ty::ConstVid<'tcx>, - ) -> Result<&'tcx ty::Const<'tcx>, ty::UniverseIndex> { + ) -> Result, ty::UniverseIndex> { match self.inner.borrow_mut().const_unification_table().probe_value(vid).val { ConstVariableValue::Known { value } => Ok(value), ConstVariableValue::Unknown { universe } => Err(universe), @@ -1487,8 +1504,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_mismatched_consts( &self, cause: &ObligationCause<'tcx>, - expected: &'tcx ty::Const<'tcx>, - actual: &'tcx ty::Const<'tcx>, + expected: ty::Const<'tcx>, + actual: ty::Const<'tcx>, err: TypeError<'tcx>, ) -> DiagnosticBuilder<'tcx> { let trace = TypeTrace::consts(cause, true, expected, actual); @@ -1585,8 +1602,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { unevaluated: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { - let mut substs = unevaluated.substs(self.tcx); - substs = self.resolve_vars_if_possible(substs); + let substs = self.resolve_vars_if_possible(unevaluated.substs); // Postpone the evaluation of constants whose substs depend on inference // variables @@ -1599,7 +1615,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let unevaluated = ty::Unevaluated { def: unevaluated.def, - substs_: Some(substs_erased), + substs: substs_erased, promoted: unevaluated.promoted, }; @@ -1743,8 +1759,8 @@ impl<'tcx> TyOrConstInferVar<'tcx> { /// Tries to extract an inference variable from a constant, returns `None` /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). - pub fn maybe_from_const(ct: &'tcx ty::Const<'tcx>) -> Option { - match ct.val { + pub fn maybe_from_const(ct: ty::Const<'tcx>) -> Option { + match ct.val() { ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), _ => None, } @@ -1764,13 +1780,13 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { self.infcx.shallow_resolve_ty(ty) } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = ct { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val() { self.infcx .inner .borrow_mut() .const_unification_table() - .probe_value(*vid) + .probe_value(vid) .val .known() .unwrap_or(ct) @@ -1791,16 +1807,22 @@ impl<'tcx> TypeTrace<'tcx> { a: Ty<'tcx>, b: Ty<'tcx>, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } } pub fn consts( cause: &ObligationCause<'tcx>, a_is_expected: bool, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } } } @@ -1817,6 +1839,7 @@ impl<'tcx> SubregionOrigin<'tcx> { ReferenceOutlivesReferent(_, a) => a, CompareImplMethodObligation { span, .. } => span, CompareImplTypeObligation { span, .. } => span, + CheckAssociatedTypeBounds { ref parent, .. } => parent.span(), } } @@ -1847,6 +1870,15 @@ impl<'tcx> SubregionOrigin<'tcx> { trait_item_def_id, }, + traits::ObligationCauseCode::CheckAssociatedTypeBounds { + impl_item_def_id, + trait_item_def_id, + } => SubregionOrigin::CheckAssociatedTypeBounds { + impl_item_def_id, + trait_item_def_id, + parent: Box::new(default()), + }, + _ => default(), } } diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index ebc0e80cdf..60f776d8c1 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -87,7 +87,7 @@ pub trait TypeRelatingDelegate<'tcx> { info: ty::VarianceDiagInfo<'tcx>, ); - fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>); + fn const_equate(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); /// Creates a new universe index. Used when instantiating placeholders. fn create_next_universe(&mut self) -> ty::UniverseIndex; @@ -201,7 +201,6 @@ where }; value.skip_binder().visit_with(&mut ScopeInstantiator { - tcx: self.infcx.tcx, next_region: &mut next_region, target_index: ty::INNERMOST, bound_region_scope: &mut scope, @@ -245,8 +244,8 @@ where scopes: &[BoundRegionScope<'tcx>], ) -> ty::Region<'tcx> { debug!("replace_bound_regions(scopes={:?})", scopes); - if let ty::ReLateBound(debruijn, br) = r { - Self::lookup_bound_region(*debruijn, br, first_free_index, scopes) + if let ty::ReLateBound(debruijn, br) = *r { + Self::lookup_bound_region(debruijn, &br, first_free_index, scopes) } else { r } @@ -451,7 +450,7 @@ impl<'tcx> VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { where D: TypeRelatingDelegate<'tcx>, { - relate.relate(&generalized_ty, &self.value_ty()) + relate.relate(generalized_ty, self.value_ty()) } } @@ -483,7 +482,7 @@ impl<'tcx> VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { where D: TypeRelatingDelegate<'tcx>, { - relate.relate(&self.value_ty(), &generalized_ty) + relate.relate(self.value_ty(), generalized_ty) } } @@ -610,16 +609,16 @@ where fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - mut b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + mut b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { let a = self.infcx.shallow_resolve(a); if !D::forbid_inference_vars() { b = self.infcx.shallow_resolve(b); } - match b.val { + match b.val() { ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { // Forbid inference variables in the RHS. bug!("unexpected inference var {:?}", b) @@ -746,7 +745,7 @@ impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, { - fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { self.delegate.const_equate(a, b); } } @@ -759,7 +758,6 @@ where /// `for<..`>. For each of those, it creates an entry in /// `bound_region_scope`. struct ScopeInstantiator<'me, 'tcx> { - tcx: TyCtxt<'tcx>, next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>, // The debruijn index of the scope we are instantiating. target_index: ty::DebruijnIndex, @@ -767,10 +765,6 @@ struct ScopeInstantiator<'me, 'tcx> { } impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - fn visit_binder>( &mut self, t: &ty::Binder<'tcx, T>, @@ -785,9 +779,9 @@ impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { let ScopeInstantiator { bound_region_scope, next_region, .. } = self; - match r { - ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => { - bound_region_scope.map.entry(*br).or_insert_with(|| next_region(*br)); + match *r { + ty::ReLateBound(debruijn, br) if debruijn == self.target_index => { + bound_region_scope.map.entry(br).or_insert_with(|| next_region(br)); } _ => {} @@ -969,8 +963,8 @@ where ) -> RelateResult<'tcx, ty::Region<'tcx>> { debug!("TypeGeneralizer::regions(a={:?})", a); - if let ty::ReLateBound(debruijn, _) = a { - if *debruijn < self.first_free_index { + if let ty::ReLateBound(debruijn, _) = *a { + if debruijn < self.first_free_index { return Ok(a); } } @@ -998,10 +992,10 @@ where fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - _: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - match a.val { + a: ty::Const<'tcx>, + _: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + match a.val() { ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); } @@ -1016,7 +1010,7 @@ where origin: var_value.origin, val: ConstVariableValue::Unknown { universe: self.universe }, }); - Ok(self.tcx().mk_const_var(new_var_id, a.ty)) + Ok(self.tcx().mk_const_var(new_var_id, a.ty())) } } } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index c2ef0b41e2..e7dca94806 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -316,7 +316,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, op: |r| { self.member_constraint( opaque_type_key.def_id, @@ -328,6 +327,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }, }); } + + fn opaque_type_origin(&self, def_id: LocalDefId) -> Option { + let tcx = self.tcx; + let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let parent_def_id = self.defining_use_anchor?; + let item_kind = &tcx.hir().expect_item(def_id).kind; + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { + span_bug!( + tcx.def_span(def_id), + "weird opaque type: {:#?}", + item_kind + ) + }; + let in_definition_scope = match *origin { + // Async `impl Trait` + hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, + // Anonymous `impl Trait` + hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, + // Named `type Foo = impl Bar;` + hir::OpaqueTyOrigin::TyAlias => { + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) + } + }; + in_definition_scope.then_some(*origin) + } } // Visitor that requires that (almost) all regions in the type visited outlive @@ -343,19 +367,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // // We ignore any type parameters because impl trait values are assumed to // capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> { - tcx: TyCtxt<'tcx>, +struct ConstrainOpaqueTypeRegionVisitor { op: OP, } -impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor where OP: FnMut(ty::Region<'tcx>), { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - fn visit_binder>( &mut self, t: &ty::Binder<'tcx, T>, @@ -377,7 +396,7 @@ where fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { // We're only interested in types involving regions - if !ty.flags().intersects(ty::TypeFlags::HAS_POTENTIAL_FREE_REGIONS) { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { return ControlFlow::CONTINUE; } @@ -459,31 +478,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // } // ``` if let Some(def_id) = def_id.as_local() { - let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_def_id = self.infcx.defining_use_anchor; - let item_kind = &tcx.hir().expect_item(def_id).kind; - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { - span_bug!( - self.value_span, - "weird opaque type: {:#?}, {:#?}", - ty.kind(), - item_kind - ) - }; - let in_definition_scope = match *origin { - // Async `impl Trait` - hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, - // Anonymous `impl Trait` - hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, - // Named `type Foo = impl Bar;` - hir::OpaqueTyOrigin::TyAlias => { - may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) - } - }; - if in_definition_scope { + if let Some(origin) = self.infcx.opaque_type_origin(def_id) { let opaque_type_key = OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; - return self.fold_opaque_ty(ty, opaque_type_key, *origin); + return self.fold_opaque_ty(ty, opaque_type_key, origin); } debug!( @@ -551,17 +549,35 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let predicate = predicate.subst(tcx, substs); debug!(?predicate); + let predicate = predicate.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| match *ty.kind() { + // Replace all other mentions of the same opaque type with the hidden type, + // as the bounds must hold on the hidden type after all. + ty::Opaque(def_id2, substs2) if def_id == def_id2 && substs == substs2 => { + ty_var + } + // Instantiate nested instances of `impl Trait`. + ty::Opaque(..) => self.instantiate_opaque_types_in_map(ty), + _ => ty, + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }); + // We can't normalize associated types from `rustc_infer`, but we can eagerly register inference variables for them. let predicate = predicate.fold_with(&mut BottomUpFolder { tcx, ty_op: |ty| match ty.kind() { - ty::Projection(projection_ty) => infcx.infer_projection( - self.param_env, - *projection_ty, - traits::ObligationCause::misc(self.value_span, self.body_id), - 0, - &mut self.obligations, - ), + ty::Projection(projection_ty) if !projection_ty.has_escaping_bound_vars() => { + infcx.infer_projection( + self.param_env, + *projection_ty, + traits::ObligationCause::misc(self.value_span, self.body_id), + 0, + &mut self.obligations, + ) + } _ => ty, }, lt_op: |lt| lt, @@ -570,15 +586,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!(?predicate); 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. + if projection.term.references_error() { return tcx.ty_error(); } } - // Change the predicate to refer to the type variable, - // which will be the concrete type instead of the opaque type. - // This also instantiates nested instances of `impl Trait`. - let predicate = self.instantiate_opaque_types_in_map(predicate); let cause = traits::ObligationCause::new(self.value_span, self.body_id, traits::OpaqueType); @@ -619,7 +630,7 @@ fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: LocalDefId, opaque_hir_id: hi let scope = tcx.hir().get_defining_scope(opaque_hir_id); // We walk up the node tree until we hit the root or the scope of the opaque type. while hir_id != scope && hir_id != hir::CRATE_HIR_ID { - hir_id = tcx.hir().get_parent_item(hir_id); + hir_id = tcx.hir().local_def_id_to_hir_id(tcx.hir().get_parent_item(hir_id)); } // Syntactically, we are allowed to define the concrete type if: let res = hir_id == scope; diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index 22e18deac2..fbf149a478 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -196,7 +196,7 @@ fn compute_components_recursive<'tcx>( out: &mut SmallVec<[Component<'tcx>; 4]>, visited: &mut SsoHashSet>, ) { - for child in parent.walk_shallow(tcx, visited) { + for child in parent.walk_shallow(visited) { match child.unpack() { GenericArgKind::Type(ty) => { compute_components(tcx, ty, out, visited); diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 3947282aa6..bd8bb9e1fa 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -2,8 +2,9 @@ use crate::infer::free_regions::FreeRegionMap; use crate::infer::{GenericKind, InferCtxt}; use crate::traits::query::OutlivesBound; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::intern::Interned; use rustc_hir as hir; -use rustc_middle::ty; +use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region}; use super::explicit_outlives_bounds; @@ -66,7 +67,7 @@ pub struct OutlivesEnvironment<'tcx> { /// "Region-bound pairs" tracks outlives relations that are known to /// be true, either because of explicit where-clauses like `T: 'a` or /// because of implied bounds. -pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>; +pub type RegionBoundPairs<'tcx> = Vec<(Region<'tcx>, GenericKind<'tcx>)>; impl<'a, 'tcx> OutlivesEnvironment<'tcx> { pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { @@ -164,10 +165,10 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); match outlives_bound { OutlivesBound::RegionSubRegion( - r_a @ (&ty::ReEarlyBound(_) | &ty::ReFree(_)), - &ty::ReVar(vid_b), + r_a @ (Region(Interned(ReEarlyBound(_), _)) | Region(Interned(ReFree(_), _))), + Region(Interned(ReVar(vid_b), _)), ) => { - infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b); + infcx.expect("no infcx provided but region vars found").add_given(r_a, *vid_b); } OutlivesBound::RegionSubParam(r_a, param_b) => { self.region_bound_pairs_accum.push((r_a, GenericKind::Param(param_b))); diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 74eb263a63..0224aba01e 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -164,7 +164,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { "cannot process registered region obligations in a snapshot" ); - debug!("process_registered_region_obligations()"); + debug!(?param_env, "process_registered_region_obligations()"); let my_region_obligations = self.take_registered_region_obligations(); @@ -285,7 +285,7 @@ where let origin = origin.clone(); match component { Component::Region(region1) => { - self.delegate.push_sub_region_constraint(origin, region, region1); + self.delegate.push_sub_region_constraint(origin, region, *region1); } Component::Param(param_ty) => { self.param_ty_must_outlive(origin, region, *param_ty); @@ -356,6 +356,8 @@ where let trait_bounds: Vec<_> = self.verify_bound.projection_declared_bounds_from_trait(projection_ty).collect(); + debug!(?trait_bounds); + // Compute the bounds we can derive from the environment. This // is an "approximate" match -- in some cases, these bounds // may not apply. diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index dba73251b4..f69212c599 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -189,7 +189,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { let mut bounds = parent - .walk_shallow(self.tcx, visited) + .walk_shallow(visited) .filter_map(|child| match child.unpack() { GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)), GenericArgKind::Lifetime(lt) => { diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index 9b53ab72b0..b45a6514d7 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -26,7 +26,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { kind: TypeVariableOriginKind::NormalizeProjectionType, span: self.tcx.def_span(def_id), }); - let projection = ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, ty: ty_var }); + let projection = + ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, term: ty_var.into() }); let obligation = Obligation::with_depth( cause, recursion_depth, diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index 2d4c1e5d05..36d18aebfe 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -154,17 +154,17 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> { let scc = self.mini_graph.sccs.scc(*leak_check_node); // Set the universe of each SCC to be the minimum of its constituent universes - let universe = self.rcc.universe(region); + let universe = self.rcc.universe(*region); debug!( "assign_placeholder_values: scc={:?} universe={:?} region={:?}", scc, universe, region ); - self.scc_universes[scc].take_min(universe, region); + self.scc_universes[scc].take_min(universe, *region); // Detect those SCCs that directly contain a placeholder - if let ty::RePlaceholder(placeholder) = region { + if let ty::RePlaceholder(placeholder) = **region { if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) { - self.assign_scc_value(scc, *placeholder)?; + self.assign_scc_value(scc, placeholder)?; } } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 29775a9668..a5bd3b15c8 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -8,6 +8,7 @@ use super::{ }; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; @@ -28,7 +29,7 @@ mod leak_check; pub use rustc_middle::infer::MemberConstraint; -#[derive(Default)] +#[derive(Clone, Default)] pub struct RegionConstraintStorage<'tcx> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. var_infos: IndexVec, @@ -502,14 +503,15 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { self.make_subregion(origin, sup, sub); match (sub, sup) { - (&ty::ReVar(sub), &ty::ReVar(sup)) => { + (Region(Interned(ReVar(sub), _)), Region(Interned(ReVar(sup), _))) => { debug!("make_eqregion: unifying {:?} with {:?}", sub, sup); - self.unification_table().union(sub, sup); + self.unification_table().union(*sub, *sup); self.any_unifications = true; } - (&ty::ReVar(vid), value) | (value, &ty::ReVar(vid)) => { + (Region(Interned(ReVar(vid), _)), value) + | (value, Region(Interned(ReVar(vid), _))) => { debug!("make_eqregion: unifying {:?} with {:?}", vid, value); - self.unification_table().union_value(vid, UnifiedRegion(Some(value))); + self.unification_table().union_value(*vid, UnifiedRegion(Some(value))); self.any_unifications = true; } (_, _) => {} @@ -550,20 +552,20 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { // cannot add constraints once regions are resolved debug!("origin = {:#?}", origin); - match (sub, sup) { - (&ReLateBound(..), _) | (_, &ReLateBound(..)) => { + match (*sub, *sup) { + (ReLateBound(..), _) | (_, ReLateBound(..)) => { span_bug!(origin.span(), "cannot relate bound region: {:?} <= {:?}", sub, sup); } - (_, &ReStatic) => { + (_, ReStatic) => { // all regions are subregions of static, so we can ignore this } - (&ReVar(sub_id), &ReVar(sup_id)) => { + (ReVar(sub_id), ReVar(sup_id)) => { self.add_constraint(Constraint::VarSubVar(sub_id, sup_id), origin); } - (_, &ReVar(sup_id)) => { + (_, ReVar(sup_id)) => { self.add_constraint(Constraint::RegSubVar(sub, sup_id), origin); } - (&ReVar(sub_id), _) => { + (ReVar(sub_id), _) => { self.add_constraint(Constraint::VarSubReg(sub_id, sup), origin); } _ => { @@ -591,16 +593,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { ) -> Region<'tcx> { // cannot add constraints once regions are resolved debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b); - match (a, b) { - (r @ &ReStatic, _) | (_, r @ &ReStatic) => { - r // nothing lives longer than static - } - - _ if a == b => { - a // LUB(a,a) = a - } - - _ => self.combine_vars(tcx, Lub, a, b, origin), + if a.is_static() || b.is_static() { + a // nothing lives longer than static + } else if a == b { + a // LUB(a,a) = a + } else { + self.combine_vars(tcx, Lub, a, b, origin) } } @@ -613,16 +611,14 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { ) -> Region<'tcx> { // cannot add constraints once regions are resolved debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b); - match (a, b) { - (&ReStatic, r) | (r, &ReStatic) => { - r // static lives longer than everything else - } - - _ if a == b => { - a // GLB(a,a) = a - } - - _ => self.combine_vars(tcx, Glb, a, b, origin), + if a.is_static() { + b // static lives longer than everything else + } else if b.is_static() { + a // static lives longer than everything else + } else if a == b { + a // GLB(a,a) = a + } else { + self.combine_vars(tcx, Glb, a, b, origin) } } @@ -639,11 +635,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { tcx: TyCtxt<'tcx>, region: ty::Region<'tcx>, ) -> ty::Region<'tcx> { - match region { + match *region { ty::ReVar(rid) => { - let unified_region = self.unification_table().probe_value(*rid); + let unified_region = self.unification_table().probe_value(rid); unified_region.0.unwrap_or_else(|| { - let root = self.unification_table().find(*rid).vid; + let root = self.unification_table().find(rid).vid; tcx.reuse_or_mk_region(region, ty::ReVar(root)) }) } @@ -767,8 +763,7 @@ impl<'tcx> VerifyBound<'tcx> { pub fn must_hold(&self) -> bool { match self { VerifyBound::IfEq(..) => false, - VerifyBound::OutlivedBy(ty::ReStatic) => true, - VerifyBound::OutlivedBy(_) => false, + VerifyBound::OutlivedBy(re) => re.is_static(), VerifyBound::IsEmpty => false, VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index f036e1214a..08358bf506 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -39,7 +39,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { } } - fn fold_const(&mut self, ct: &'tcx Const<'tcx>) -> &'tcx Const<'tcx> { + fn fold_const(&mut self, ct: Const<'tcx>) -> Const<'tcx> { if !ct.has_infer_types_or_consts() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { @@ -98,7 +98,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticRegionResolver<'a, 'tcx> { } } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { if !ct.has_infer_regions() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { @@ -126,11 +126,6 @@ impl<'a, 'tcx> UnresolvedTypeFinder<'a, 'tcx> { impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { type BreakTy = (Ty<'tcx>, Option); - - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.infcx.tcx) - } - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { let t = self.infcx.shallow_resolve(t); if t.has_infer_types() { @@ -223,15 +218,12 @@ impl<'a, 'tcx> FallibleTypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { } } - fn try_fold_const( - &mut self, - c: &'tcx ty::Const<'tcx>, - ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { + fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result, Self::Error> { if !c.needs_infer() { Ok(c) // micro-optimize -- if there is nothing in this const that this fold affects... } else { let c = self.infcx.shallow_resolve(c); - match c.val { + match c.val() { ty::ConstKind::Infer(InferConst::Var(vid)) => { return Err(FixupError::UnresolvedConst(vid)); } diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index ccac0efd6c..9ec1b3390d 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -151,9 +151,9 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { self.fields.infcx.super_combine_consts(self, a, b) } @@ -170,7 +170,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { } impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { self.fields.add_const_equate_obligation(self.a_is_expected, a, b); } } diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 82970f214f..74c4408904 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -14,6 +14,7 @@ use std::ops::Range; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; /// Represents a single undo-able action that affects a type inference variable. +#[derive(Clone)] pub(crate) enum UndoLog<'tcx> { EqRelation(sv::UndoLog>>), SubRelation(sv::UndoLog>), @@ -58,6 +59,7 @@ impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { } } +#[derive(Clone)] pub struct TypeVariableStorage<'tcx> { values: sv::SnapshotVecStorage, @@ -137,6 +139,7 @@ pub enum TypeVariableOriginKind { LatticeVariable, } +#[derive(Clone)] pub(crate) struct TypeVariableData { origin: TypeVariableOrigin, } @@ -165,6 +168,7 @@ impl<'tcx> TypeVariableValue<'tcx> { } } +#[derive(Clone)] pub(crate) struct Instantiate; pub(crate) struct Delegate; @@ -259,7 +263,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { let index = self.values().push(TypeVariableData { origin }); assert_eq!(eq_key.vid.as_u32(), index as u32); - debug!("new_var(index={:?}, universe={:?}, origin={:?}", eq_key.vid, universe, origin,); + debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin); eq_key.vid } @@ -412,6 +416,7 @@ impl<'tcx> ut::UnifyKey for TyVidEqKey<'tcx> { fn index(&self) -> u32 { self.vid.as_u32() } + #[inline] fn from_index(i: u32) -> Self { TyVidEqKey::from(ty::TyVid::from_u32(i)) } diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs index 89db8f464b..ecd886b547 100644 --- a/compiler/rustc_infer/src/infer/undo_log.rs +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -17,6 +17,7 @@ pub struct Snapshot<'tcx> { } /// Records the "undo" data for a single operation that affects some form of inference variable. +#[derive(Clone)] pub(crate) enum UndoLog<'tcx> { TypeVariables(type_variable::UndoLog<'tcx>), ConstUnificationTable(sv::UndoLog>>), @@ -84,6 +85,7 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { /// The combined undo log for all the various unification tables. For each change to the storage /// for any kind of inference variable, we record an UndoLog entry in the vector here. +#[derive(Clone)] pub(crate) struct InferCtxtUndoLogs<'tcx> { logs: Vec>, num_open_snapshots: usize, diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 5f228d1e20..ae79e14db1 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -23,6 +23,7 @@ #![feature(min_specialization)] #![feature(label_break_value)] #![recursion_limit = "512"] // For rustdoc +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index e1f3b548e9..85bb727a6c 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -101,7 +101,7 @@ pub enum FulfillmentErrorCode<'tcx> { CodeSelectionError(SelectionError<'tcx>), CodeProjectionError(MismatchedProjectionTypes<'tcx>), CodeSubtypeError(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate - CodeConstEquateError(ExpectedFound<&'tcx Const<'tcx>>, TypeError<'tcx>), + CodeConstEquateError(ExpectedFound>, TypeError<'tcx>), CodeAmbiguity, } diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index 96af16c668..b84ed3dc68 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -70,7 +70,7 @@ pub struct ProjectionCache<'a, 'tcx> { undo_log: &'a mut InferCtxtUndoLogs<'tcx>, } -#[derive(Default)] +#[derive(Clone, Default)] pub struct ProjectionCacheStorage<'tcx> { map: SnapshotMapStorage, ProjectionCacheEntry<'tcx>>, } @@ -93,7 +93,7 @@ pub enum ProjectionCacheEntry<'tcx> { Recur, Error, NormalizedTy { - ty: NormalizedTy<'tcx>, + ty: Normalized<'tcx, ty::Term<'tcx>>, /// If we were able to successfully evaluate the /// corresponding cache entry key during predicate /// evaluation, then this field stores the final @@ -174,7 +174,11 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { } /// Indicates that `key` was normalized to `value`. - pub fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) { + pub fn insert_term( + &mut self, + key: ProjectionCacheKey<'tcx>, + value: Normalized<'tcx, ty::Term<'tcx>>, + ) { debug!( "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}", key, value diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 8f5d6c8509..674c75fdee 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -152,7 +152,7 @@ impl<'tcx> Elaborator<'tcx> { obligation.cause.clone(), ) }); - debug!("super_predicates: data={:?}", data); + debug!(?data, ?obligations, "super_predicates"); // Only keep those bounds that we haven't already seen. // This is necessary to prevent infinite recursion in some @@ -241,10 +241,19 @@ impl<'tcx> Elaborator<'tcx> { Component::UnresolvedInferenceVariable(_) => None, - Component::Projection(_) | Component::EscapingProjection(_) => { - // We can probably do more here. This - // corresponds to a case like `>::U: 'b`. + Component::Projection(projection) => { + // We might end up here if we have `Foo<::Assoc>: 'a`. + // With this, we can deduce that `::Assoc: 'a`. + let ty = + tcx.mk_projection(projection.item_def_id, projection.substs); + Some(ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty, r_min, + ))) + } + + Component::EscapingProjection(_) => { + // We might be able to do more here, but we don't + // want to deal with escaping vars right now. None } }) diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index f5823e521b..e31119c129 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -10,8 +10,8 @@ doctest = false libc = "0.2" libloading = "0.7.1" tracing = "0.1" -rustc-rayon-core = "0.3.1" -rayon = { version = "0.3.1", package = "rustc-rayon" } +rustc-rayon-core = "0.3.2" +rayon = { version = "0.3.2", package = "rustc-rayon" } smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 3c7908fae7..a18e2d1d63 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -4,7 +4,7 @@ //! `rustc_data_structures::AtomicRef` type, which allows us to setup a global //! static which can then be set in this file at program startup. //! -//! See `SPAN_DEBUG` for an example of how to set things up. +//! See `SPAN_TRACK` for an example of how to set things up. //! //! The functions in this file should fall back to the default set in their //! origin crate when the `TyCtxt` is not present in TLS. @@ -13,18 +13,6 @@ use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS}; use rustc_middle::ty::tls; use std::fmt; -/// This is a callback from `rustc_ast` as it cannot access the implicit state -/// in `rustc_middle` otherwise. -fn span_debug(span: rustc_span::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { - tls::with_opt(|tcx| { - if let Some(tcx) = tcx { - rustc_span::debug_with_source_map(span, f, tcx.sess.source_map()) - } else { - rustc_span::default_span_debug(span, f) - } - }) -} - fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { tls::with_opt(|tcx| { if let Some(tcx) = tcx { @@ -65,7 +53,6 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> /// Sets up the callbacks in prior crates which we want to refer to the /// TyCtxt in. pub fn setup_callbacks() { - rustc_span::SPAN_DEBUG.swap(&(span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_))); rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); TRACK_DIAGNOSTICS.swap(&(track_diagnostic as fn(&_))); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 3804e10030..609fc4b78c 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -2,7 +2,7 @@ pub use crate::passes::BoxedResolver; use crate::util; use rustc_ast::token; -use rustc_ast::{self as ast, MetaItemKind}; +use rustc_ast::{self as ast, LitKind, MetaItemKind}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; @@ -13,15 +13,15 @@ use rustc_lint::LintStore; use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; use rustc_query_impl::QueryCtxt; -use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames}; +use rustc_session::config::{self, CheckCfg, ErrorOutputType, Input, OutputFilenames}; use rustc_session::early_error; use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; use rustc_session::{DiagnosticOutput, Session}; use rustc_span::source_map::{FileLoader, FileName}; +use rustc_span::symbol::sym; use std::path::PathBuf; use std::result; -use std::sync::{Arc, Mutex}; pub type Result = result::Result; @@ -124,13 +124,106 @@ pub fn parse_cfgspecs(cfgspecs: Vec) -> FxHashSet<(String, Option errs.into_iter().for_each(|mut err| err.cancel()), } - error!(r#"expected `key` or `key="value"`"#); + // If the user tried to use a key="value" flag, but is missing the quotes, provide + // a hint about how to resolve this. + if s.contains('=') && !s.contains("=\"") && !s.ends_with('"') { + error!(concat!( + r#"expected `key` or `key="value"`, ensure escaping is appropriate"#, + r#" for your shell, try 'key="value"' or key=\"value\""# + )); + } else { + error!(r#"expected `key` or `key="value"`"#); + } }) .collect::(); cfg.into_iter().map(|(a, b)| (a.to_string(), b.map(|b| b.to_string()))).collect() }) } +/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`. +pub fn parse_check_cfg(specs: Vec) -> CheckCfg { + rustc_span::create_default_session_if_not_set_then(move |_| { + let mut cfg = CheckCfg::default(); + + 'specs: for s in specs { + let sess = ParseSess::with_silent_emitter(Some(format!( + "this error occurred on the command line: `--check-cfg={}`", + s + ))); + let filename = FileName::cfg_spec_source_code(&s); + + macro_rules! error { + ($reason: expr) => { + early_error( + ErrorOutputType::default(), + &format!( + concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"), + s + ), + ); + }; + } + + match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) { + Ok(mut parser) => match &mut parser.parse_meta_item() { + Ok(meta_item) if parser.token == token::Eof => { + if let Some(args) = meta_item.meta_item_list() { + if meta_item.has_name(sym::names) { + cfg.names_checked = true; + for arg in args { + if arg.is_word() && arg.ident().is_some() { + let ident = arg.ident().expect("multi-segment cfg key"); + cfg.names_valid.insert(ident.name.to_string()); + } else { + error!("`names()` arguments must be simple identifers"); + } + } + continue 'specs; + } else if meta_item.has_name(sym::values) { + if let Some((name, values)) = args.split_first() { + if name.is_word() && name.ident().is_some() { + let ident = name.ident().expect("multi-segment cfg key"); + cfg.values_checked.insert(ident.to_string()); + for val in values { + if let Some(LitKind::Str(s, _)) = + val.literal().map(|lit| &lit.kind) + { + cfg.values_valid + .insert((ident.to_string(), s.to_string())); + } else { + error!( + "`values()` arguments must be string literals" + ); + } + } + + continue 'specs; + } else { + error!( + "`values()` first argument must be a simple identifer" + ); + } + } + } + } + } + Ok(..) => {} + Err(err) => err.cancel(), + }, + Err(errs) => errs.into_iter().for_each(|mut err| err.cancel()), + } + + error!( + "expected `names(name1, name2, ... nameN)` or \ + `values(name, \"value1\", \"value2\", ... \"valueN\")`" + ); + } + + cfg.names_valid.extend(cfg.values_checked.iter().cloned()); + cfg + }) +} + /// The compiler configuration pub struct Config { /// Command line options @@ -138,6 +231,7 @@ pub struct Config { /// cfg! configuration in addition to the default ones pub crate_cfg: FxHashSet<(String, Option)>, + pub crate_check_cfg: CheckCfg, pub input: Input, pub input_path: Option, @@ -146,9 +240,6 @@ pub struct Config { pub file_loader: Option>, pub diagnostic_output: DiagnosticOutput, - /// Set to capture stderr output during compiler execution - pub stderr: Option>>>, - pub lint_caps: FxHashMap, /// This is a callback from the driver that is called when [`ParseSess`] is created. @@ -177,10 +268,13 @@ pub struct Config { } pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R) -> R { + crate::callbacks::setup_callbacks(); + let registry = &config.registry; let (mut sess, codegen_backend) = util::create_session( config.opts, config.crate_cfg, + config.crate_check_cfg, config.diagnostic_output, config.file_loader, config.input_path.clone(), @@ -226,13 +320,11 @@ pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R }) } -pub fn run_compiler(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { +pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { tracing::trace!("run_compiler"); - let stderr = config.stderr.take(); - util::setup_callbacks_and_run_in_thread_pool_with_globals( + util::run_in_thread_pool_with_globals( config.opts.edition, config.opts.debugging_opts.threads, - &stderr, || create_compiler_and_run(config, f), ) } diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 2fc3759968..dcad3036cc 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,10 +1,12 @@ #![feature(bool_to_option)] #![feature(box_patterns)] +#![feature(let_else)] #![feature(internal_output_capture)] #![feature(thread_spawn_unchecked)] #![feature(nll)] #![feature(once_cell)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] mod callbacks; pub mod interface; @@ -13,6 +15,7 @@ mod proc_macro_decls; mod queries; pub mod util; +pub use callbacks::setup_callbacks; pub use interface::{run_compiler, Config}; pub use passes::{DEFAULT_EXTERN_QUERY_PROVIDERS, DEFAULT_QUERY_PROVIDERS}; pub use queries::Queries; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 33bf670f57..c0552fd200 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -3,7 +3,7 @@ use crate::proc_macro_decls; use crate::util; use rustc_ast::mut_visit::MutVisitor; -use rustc_ast::{self as ast, visit, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, visit}; use rustc_borrowck as mir_borrowck; use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_ssa::traits::CodegenBackend; @@ -11,16 +11,16 @@ use rustc_data_structures::parallel; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{Applicability, ErrorReported, PResult}; -use rustc_expand::base::ExtCtxt; +use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand}; use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_hir::Crate; -use rustc_lint::LintStore; +use rustc_lint::{EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; use rustc_metadata::{encode_metadata, EncodedMetadata}; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::query::{ExternProviders, Providers}; -use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; +use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, ResolverOutputs, TyCtxt}; use rustc_mir_build as mir_build; use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_attr}; use rustc_passes::{self, hir_stats, layout_test}; @@ -34,7 +34,7 @@ use rustc_session::lint; use rustc_session::output::{filename_for_input, filename_for_metadata}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::{FileName, MultiSpan}; use rustc_trait_selection::traits; use rustc_typeck as typeck; @@ -233,26 +233,43 @@ pub fn register_plugins<'a>( Ok((krate, lint_store)) } -fn pre_expansion_lint( +fn pre_expansion_lint<'a>( sess: &Session, lint_store: &LintStore, - krate: &ast::Crate, - crate_attrs: &[ast::Attribute], - crate_name: &str, + registered_tools: &RegisteredTools, + check_node: impl EarlyCheckNode<'a>, + node_name: &str, ) { - sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", crate_name).run(|| { - rustc_lint::check_ast_crate( + sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", node_name).run(|| { + rustc_lint::check_ast_node( sess, - lint_store, - krate, - crate_attrs, true, + lint_store, + registered_tools, None, rustc_lint::BuiltinCombinedPreExpansionLintPass::new(), + check_node, ); }); } +// Cannot implement directly for `LintStore` due to trait coherence. +struct LintStoreExpandImpl<'a>(&'a LintStore); + +impl LintStoreExpand for LintStoreExpandImpl<'_> { + fn pre_expansion_lint( + &self, + sess: &Session, + registered_tools: &RegisteredTools, + node_id: ast::NodeId, + attrs: &[ast::Attribute], + items: &[rustc_ast::ptr::P], + name: &str, + ) { + pre_expansion_lint(sess, self.0, registered_tools, (node_id, attrs, items), name); + } +} + /// Runs the "early phases" of the compiler: initial `cfg` processing, loading compiler plugins, /// syntax expansion, secondary `cfg` expansion, synthesis of a test /// harness if one is to be provided, injection of a dependency on the @@ -265,12 +282,11 @@ pub fn configure_and_expand( resolver: &mut Resolver<'_>, ) -> Result { tracing::trace!("configure_and_expand"); - pre_expansion_lint(sess, lint_store, &krate, &krate.attrs, crate_name); + pre_expansion_lint(sess, lint_store, resolver.registered_tools(), &krate, crate_name); rustc_builtin_macros::register_builtin_macros(resolver); krate = sess.time("crate_injection", || { - let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s)); - rustc_builtin_macros::standard_library_imports::inject(krate, resolver, sess, alt_std_name) + rustc_builtin_macros::standard_library_imports::inject(krate, resolver, sess) }); util::check_attr_crate_type(sess, &krate.attrs, &mut resolver.lint_buffer()); @@ -321,13 +337,8 @@ pub fn configure_and_expand( ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; - let crate_attrs = krate.attrs.clone(); - let extern_mod_loaded = |ident: Ident, attrs, items, span| { - let krate = ast::Crate { attrs, items, span, id: DUMMY_NODE_ID, is_placeholder: false }; - pre_expansion_lint(sess, lint_store, &krate, &crate_attrs, ident.name.as_str()); - (krate.attrs, krate.items) - }; - let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&extern_mod_loaded)); + let lint_store = LintStoreExpandImpl(lint_store); + let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&lint_store)); // Expand macros now! let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); @@ -382,8 +393,18 @@ pub fn configure_and_expand( }); let crate_types = sess.crate_types(); + let is_executable_crate = crate_types.contains(&CrateType::Executable); let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); + if crate_types.len() > 1 { + if is_executable_crate { + sess.err("cannot mix `bin` crate type with others"); + } + if is_proc_macro_crate { + sess.err("cannot mix `proc-macro` crate type with others"); + } + } + // For backwards compatibility, we don't try to run proc macro injection // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being // specified. This should only affect users who manually invoke 'rustdoc', as @@ -400,7 +421,6 @@ pub fn configure_and_expand( msg.emit() } else { krate = sess.time("maybe_create_a_macro_crate", || { - let num_crate_types = crate_types.len(); let is_test_crate = sess.opts.test; rustc_builtin_macros::proc_macro_harness::inject( sess, @@ -409,7 +429,6 @@ pub fn configure_and_expand( is_proc_macro_crate, has_proc_macro_decls, is_test_crate, - num_crate_types, sess.diagnostic(), ) }); @@ -499,14 +518,15 @@ pub fn lower_to_hir<'res, 'tcx>( ); sess.time("early_lint_checks", || { - rustc_lint::check_ast_crate( + let lint_buffer = Some(std::mem::take(resolver.lint_buffer())); + rustc_lint::check_ast_node( sess, - lint_store, - &krate, - &krate.attrs, false, - Some(std::mem::take(resolver.lint_buffer())), + lint_store, + resolver.registered_tools(), + lint_buffer, rustc_lint::BuiltinCombinedEarlyLintPass::new(), + &*krate, ) }); @@ -645,13 +665,13 @@ fn write_out_deps( boxed_resolver.borrow_mut().access(|resolver| { for cnum in resolver.cstore().crates_untracked() { let source = resolver.cstore().crate_source_untracked(cnum); - if let Some((path, _)) = source.dylib { + if let Some((path, _)) = &source.dylib { files.push(escape_dep_filename(&path.display().to_string())); } - if let Some((path, _)) = source.rlib { + if let Some((path, _)) = &source.rlib { files.push(escape_dep_filename(&path.display().to_string())); } - if let Some((path, _)) = source.rmeta { + if let Some((path, _)) = &source.rmeta { files.push(escape_dep_filename(&path.display().to_string())); } } @@ -986,7 +1006,8 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { tcx.ensure().check_private_in_public(()); }, { - sess.time("death_checking", || rustc_passes::dead::check_crate(tcx)); + tcx.hir() + .par_for_each_module(|module| tcx.ensure().check_mod_deathness(module)); }, { sess.time("unused_lib_feature_checking", || { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index e635ee1e0e..89390ee1d6 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -13,7 +13,6 @@ use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::{GlobalCtxt, TyCtxt}; use rustc_query_impl::Queries as TcxQueries; -use rustc_serialize::json; use rustc_session::config::{self, OutputFilenames, OutputType}; use rustc_session::{output::find_crate_name, Session}; use rustc_span::symbol::sym; @@ -367,12 +366,10 @@ impl Linker { } if sess.opts.debugging_opts.no_link { - // FIXME: use a binary format to encode the `.rlink` file - let rlink_data = json::encode(&codegen_results).map_err(|err| { - sess.fatal(&format!("failed to encode rlink: {}", err)); - })?; + let mut encoder = rustc_serialize::opaque::Encoder::new(Vec::new()); + rustc_serialize::Encodable::encode(&codegen_results, &mut encoder).unwrap(); let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT); - std::fs::write(&rlink_file, rlink_data).map_err(|err| { + std::fs::write(&rlink_file, encoder.into_inner()).map_err(|err| { sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err)); })?; return Ok(()); @@ -403,10 +400,6 @@ impl Compiler { gcx.enter(rustc_query_impl::alloc_self_profile_query_strings); } - if self.session().opts.debugging_opts.query_stats { - gcx.enter(rustc_query_impl::print_stats); - } - self.session() .time("serialize_dep_graph", || gcx.enter(rustc_incremental::save_dep_graph)); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 30e319c475..f9c39f7f83 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -8,10 +8,11 @@ use rustc_session::config::{build_configuration, build_session_options, to_crate use rustc_session::config::{ rustc_optgroups, ErrorOutputType, ExternLocation, LocationDetail, Options, Passes, }; -use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; use rustc_session::config::{ - Externs, OutputType, OutputTypes, SymbolManglingVersion, WasiExecModel, + BranchProtection, Externs, OutputType, OutputTypes, PAuthKey, PacRet, SymbolManglingVersion, + WasiExecModel, }; +use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; @@ -574,6 +575,7 @@ fn test_codegen_options_tracking_hash() { tracked!(force_frame_pointers, Some(false)); tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); + tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(link_dead_code, Some(true)); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); @@ -645,6 +647,7 @@ fn test_debugging_options_tracking_hash() { untracked!(borrowck, String::from("other")); untracked!(deduplicate_diagnostics, false); untracked!(dep_tasks, true); + untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe"))); untracked!(dont_buffer_diagnostics, true); untracked!(dump_dep_graph, true); untracked!(dump_mir, Some(String::from("abc"))); @@ -676,13 +679,11 @@ fn test_debugging_options_tracking_hash() { // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); untracked!(profile_closures, true); - untracked!(print_link_args, true); untracked!(print_llvm_passes, true); untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_type_sizes, true); untracked!(proc_macro_backtrace, true); untracked!(query_dep_graph, true); - untracked!(query_stats, true); untracked!(save_analysis, true); untracked!(self_profile, SwitchWithOptPath::Enabled(None)); untracked!(self_profile_events, Some(vec![String::new()])); @@ -718,12 +719,20 @@ fn test_debugging_options_tracking_hash() { tracked!(asm_comments, true); tracked!(assume_incomplete_release, true); tracked!(binary_dep_depinfo, true); + tracked!( + branch_protection, + Some(BranchProtection { + bti: true, + pac_ret: Some(PacRet { leaf: true, key: PAuthKey::B }) + }) + ); tracked!(chalk, true); tracked!(codegen_backend, Some("abc".to_string())); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(debug_info_for_profiling, true); tracked!(debug_macros, true); tracked!(dep_info_omit_d_target, true); + tracked!(drop_tracking, true); tracked!(dual_proc_macros, true); tracked!(fewer_names, Some(true)); tracked!(force_unstable_if_unmarked, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index cb51555f5c..46964f5268 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -1,7 +1,7 @@ use libloading::Library; use rustc_ast::mut_visit::{visit_clobber, MutVisitor, *}; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, AttrVec, BlockCheckMode}; +use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Term}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[cfg(parallel_compiler)] @@ -15,6 +15,7 @@ use rustc_parse::validate_attr; use rustc_query_impl::QueryCtxt; use rustc_resolve::{self, Resolver}; use rustc_session as session; +use rustc_session::config::CheckCfg; use rustc_session::config::{self, CrateType}; use rustc_session::config::{ErrorOutputType, Input, OutputFilenames}; use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; @@ -27,7 +28,6 @@ use rustc_span::symbol::{sym, Symbol}; use smallvec::SmallVec; use std::env; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; -use std::io; use std::lazy::SyncOnceCell; use std::mem; use std::ops::DerefMut; @@ -35,7 +35,6 @@ use std::ops::DerefMut; use std::panic; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; use std::thread; use tracing::info; @@ -67,6 +66,7 @@ pub fn add_configuration( pub fn create_session( sopts: config::Options, cfg: FxHashSet<(String, Option)>, + check_cfg: CheckCfg, diagnostic_output: DiagnosticOutput, file_loader: Option>, input_path: Option, @@ -102,7 +102,13 @@ pub fn create_session( let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg)); add_configuration(&mut cfg, &mut sess, &*codegen_backend); + + let mut check_cfg = config::to_crate_check_config(check_cfg); + check_cfg.fill_well_known(); + check_cfg.fill_actual(&cfg); + sess.parse_sess.config = cfg; + sess.parse_sess.check_config = check_cfg; (Lrc::new(sess), Lrc::new(codegen_backend)) } @@ -118,7 +124,7 @@ fn get_stack_size() -> Option { /// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need /// for `'static` bounds. #[cfg(not(parallel_compiler))] -pub fn scoped_thread R + Send, R: Send>(cfg: thread::Builder, f: F) -> R { +fn scoped_thread R + Send, R: Send>(cfg: thread::Builder, f: F) -> R { // SAFETY: join() is called immediately, so any closure captures are still // alive. match unsafe { cfg.spawn_unchecked(f) }.unwrap().join() { @@ -128,10 +134,9 @@ pub fn scoped_thread R + Send, R: Send>(cfg: thread::Builder, f: } #[cfg(not(parallel_compiler))] -pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Send, R: Send>( +pub fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, _threads: usize, - stderr: &Option>>>, f: F, ) -> R { let mut cfg = thread::Builder::new().name("rustc".to_string()); @@ -140,14 +145,7 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Se cfg = cfg.stack_size(size); } - crate::callbacks::setup_callbacks(); - - let main_handler = move || { - rustc_span::create_session_globals_then(edition, || { - io::set_output_capture(stderr.clone()); - f() - }) - }; + let main_handler = move || rustc_span::create_session_globals_then(edition, f); scoped_thread(cfg, main_handler) } @@ -176,14 +174,11 @@ unsafe fn handle_deadlock() { } #[cfg(parallel_compiler)] -pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Send, R: Send>( +pub fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, threads: usize, - stderr: &Option>>>, f: F, ) -> R { - crate::callbacks::setup_callbacks(); - let mut config = rayon::ThreadPoolBuilder::new() .thread_name(|_| "rustc".to_string()) .acquire_thread_handler(jobserver::acquire_thread) @@ -203,10 +198,7 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Se // the thread local rustc uses. `session_globals` is captured and set // on the new threads. let main_handler = move |thread: rayon::ThreadBuilder| { - rustc_span::set_session_globals_then(session_globals, || { - io::set_output_capture(stderr.clone()); - thread.run() - }) + rustc_span::set_session_globals_then(session_globals, || thread.run()) }; config.build_scoped(main_handler, with_pool).unwrap() @@ -343,6 +335,7 @@ fn sysroot_candidates() -> Vec { #[cfg(windows)] fn current_dll_path() -> Option { use std::ffi::OsString; + use std::io; use std::os::windows::prelude::*; use std::ptr; @@ -379,7 +372,7 @@ fn sysroot_candidates() -> Vec { } } -pub fn get_codegen_sysroot(maybe_sysroot: &Option, backend_name: &str) -> MakeBackendFn { +fn get_codegen_sysroot(maybe_sysroot: &Option, backend_name: &str) -> MakeBackendFn { // For now we only allow this function to be called once as it'll dlopen a // few things, which seems to work best if we only do that once. In // general this assertion never trips due to the once guard in `get_codegen_backend`, @@ -717,52 +710,57 @@ impl<'a, 'b> ReplaceBodyWithLoop<'a, 'b> { } fn should_ignore_fn(ret_ty: &ast::FnRetTy) -> bool { - if let ast::FnRetTy::Ty(ref ty) = ret_ty { - fn involves_impl_trait(ty: &ast::Ty) -> bool { - match ty.kind { - ast::TyKind::ImplTrait(..) => true, - ast::TyKind::Slice(ref subty) - | ast::TyKind::Array(ref subty, _) - | ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) - | ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) - | ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), - ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), - ast::TyKind::Path(_, ref path) => { - path.segments.iter().any(|seg| match seg.args.as_deref() { - None => false, - Some(&ast::GenericArgs::AngleBracketed(ref data)) => { - data.args.iter().any(|arg| match arg { - ast::AngleBracketedArg::Arg(arg) => match arg { - ast::GenericArg::Type(ty) => involves_impl_trait(ty), - ast::GenericArg::Lifetime(_) - | ast::GenericArg::Const(_) => false, - }, - ast::AngleBracketedArg::Constraint(c) => match c.kind { - ast::AssocTyConstraintKind::Bound { .. } => true, - ast::AssocTyConstraintKind::Equality { ref ty } => { - involves_impl_trait(ty) + let ast::FnRetTy::Ty(ref ty) = ret_ty else { + return false; + }; + fn involves_impl_trait(ty: &ast::Ty) -> bool { + match ty.kind { + ast::TyKind::ImplTrait(..) => true, + ast::TyKind::Slice(ref subty) + | ast::TyKind::Array(ref subty, _) + | ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) + | ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) + | ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), + ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), + ast::TyKind::Path(_, ref path) => { + path.segments.iter().any(|seg| match seg.args.as_deref() { + None => false, + Some(&ast::GenericArgs::AngleBracketed(ref data)) => { + data.args.iter().any(|arg| match arg { + ast::AngleBracketedArg::Arg(arg) => match arg { + ast::GenericArg::Type(ty) => involves_impl_trait(ty), + ast::GenericArg::Lifetime(_) | ast::GenericArg::Const(_) => { + false + } + }, + ast::AngleBracketedArg::Constraint(c) => match c.kind { + ast::AssocConstraintKind::Bound { .. } => true, + ast::AssocConstraintKind::Equality { ref term } => { + match term { + Term::Ty(ty) => involves_impl_trait(ty), + // FIXME(...): This should check if the constant + // involves a trait impl, but for now ignore. + Term::Const(_) => false, } - }, - }) - } - Some(&ast::GenericArgs::Parenthesized(ref data)) => { - any_involves_impl_trait(data.inputs.iter()) - || ReplaceBodyWithLoop::should_ignore_fn(&data.output) - } - }) - } - _ => false, + } + }, + }) + } + Some(&ast::GenericArgs::Parenthesized(ref data)) => { + any_involves_impl_trait(data.inputs.iter()) + || ReplaceBodyWithLoop::should_ignore_fn(&data.output) + } + }) } + _ => false, } + } - fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { - it.any(|subty| involves_impl_trait(subty)) - } - - involves_impl_trait(ty) - } else { - false + fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { + it.any(|subty| involves_impl_trait(subty)) } + + involves_impl_trait(ty) } fn is_sig_const(sig: &ast::FnSig) -> bool { diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index b615175758..a14d602036 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { } // We only care about method call expressions. - if let hir::ExprKind::MethodCall(call, span, args, _) = &expr.kind { + if let hir::ExprKind::MethodCall(call, args, _) = &expr.kind { if call.ident.name != sym::into_iter { return; } @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { // to an array or to a slice. _ => bug!("array type coerced to something other than array or slice"), }; - cx.struct_span_lint(ARRAY_INTO_ITER, *span, |lint| { + cx.struct_span_lint(ARRAY_INTO_ITER, call.ident.span, |lint| { let mut diag = lint.build(&format!( "this method call resolves to `<&{} as IntoIterator>::into_iter` \ (due to backwards compatibility), \ diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 1fd6379b6e..a397db7f32 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -152,8 +152,8 @@ declare_lint! { declare_lint_pass!(BoxPointers => [BOX_POINTERS]); impl BoxPointers { - fn check_heap_type<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { - for leaf in ty.walk(cx.tcx) { + fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) { + for leaf in ty.walk() { if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { if leaf_ty.is_box() { cx.struct_span_lint(BOX_POINTERS, span, |lint| { @@ -610,8 +610,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { // reported for missing docs. let real_trait = trait_ref.path.res.def_id(); let Some(def_id) = real_trait.as_local() else { return }; - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); - let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) else { return }; + let Some(Node::Item(item)) = cx.tcx.hir().find_by_def_id(def_id) else { return }; if let hir::VisibilityKind::Inherited = item.vis.node { for impl_item_ref in items { self.private_traits.insert(impl_item_ref.id.hir_id()); @@ -656,7 +655,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { // If the method is an impl for an item with docs_hidden, don't doc. if method_context(cx, impl_item.hir_id()) == MethodLateContext::PlainImpl { - let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id()); + let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()); let impl_ty = cx.tcx.type_of(parent); let outerdef = match impl_ty.kind() { ty::Adt(def, _) => Some(def.did), @@ -913,7 +912,7 @@ declare_lint_pass!( impl EarlyLintPass for AnonymousParameters { fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { - if cx.sess.edition() != Edition::Edition2015 { + if cx.sess().edition() != Edition::Edition2015 { // This is a hard error in future editions; avoid linting and erroring return; } @@ -922,7 +921,7 @@ impl EarlyLintPass for AnonymousParameters { if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { 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); + let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span); let (ty_snip, appl) = if let Ok(ref snip) = ty_snip { (snip.as_str(), Applicability::MachineApplicable) @@ -1212,7 +1211,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { check_no_mangle_on_generic_fn( no_mangle_attr, Some(generics), - cx.tcx.hir().get_generics(it.id.def_id.to_def_id()).unwrap(), + cx.tcx.hir().get_generics(it.id.def_id).unwrap(), it.span, ); } @@ -1248,7 +1247,7 @@ declare_lint! { /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html MUTABLE_TRANSMUTES, Deny, - "mutating transmuted &mut T from &T may cause undefined behavior" + "transmuting &T to &mut T is undefined behavior, even if the reference is unused" } declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]); @@ -1260,8 +1259,8 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind())) { if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { - let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \ - consider instead using an UnsafeCell"; + let msg = "transmuting &T to &mut T is undefined behavior, \ + even if the reference is unused, consider instead using an UnsafeCell"; cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| lint.build(msg).emit()); } } @@ -1480,12 +1479,6 @@ impl TypeAliasBounds { err: &'a mut DiagnosticBuilder<'db>, } impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) { if TypeAliasBounds::is_type_variable_assoc(qpath) { self.err.span_help( @@ -1663,7 +1656,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { ConstEquate(..) | TypeWellFormedFromEnv(..) => continue, }; - if predicate.is_global(cx.tcx) { + if predicate.is_global() { cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| { lint.build(&format!( "{} bound {} does not depend on any type \ @@ -1782,7 +1775,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { }; if join.edition() >= Edition::Edition2021 { let mut err = - rustc_errors::struct_span_err!(cx.sess, pat.span, E0783, "{}", msg,); + rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,); err.span_suggestion( pat.span, suggestion, @@ -1806,7 +1799,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { let replace = "..=".to_owned(); if join.edition() >= Edition::Edition2021 { let mut err = - rustc_errors::struct_span_err!(cx.sess, pat.span, E0783, "{}", msg,); + rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,); err.span_suggestion_short( join, suggestion, @@ -1990,7 +1983,7 @@ impl KeywordIdents { UnderMacro(under_macro): UnderMacro, ident: Ident, ) { - let next_edition = match cx.sess.edition() { + let next_edition = match cx.sess().edition() { Edition::Edition2015 => { match ident.name { kw::Async | kw::Await | kw::Try => Edition::Edition2018, @@ -2018,7 +2011,7 @@ impl KeywordIdents { }; // Don't lint `r#foo`. - if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) { + if cx.sess().parse_sess.raw_identifier_spans.borrow().contains(&ident.span) { return; } @@ -2057,7 +2050,7 @@ impl ExplicitOutlivesRequirements { inferred_outlives .iter() .filter_map(|(pred, _)| match pred.kind().skip_binder() { - ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a { + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { ty::ReEarlyBound(ebr) if ebr.index == index => Some(b), _ => None, }, @@ -2118,10 +2111,10 @@ impl ExplicitOutlivesRequirements { if let hir::GenericBound::Outlives(lifetime) = bound { let is_inferred = match tcx.named_region(lifetime.hir_id) { Some(Region::Static) if infer_static => { - inferred_outlives.iter().any(|r| matches!(r, ty::ReStatic)) + inferred_outlives.iter().any(|r| matches!(**r, ty::ReStatic)) } Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| { - if let ty::ReEarlyBound(ebr) = r { ebr.index == index } else { false } + if let ty::ReEarlyBound(ebr) = **r { ebr.index == index } else { false } }), _ => false, }; @@ -2386,7 +2379,7 @@ declare_lint_pass!( impl EarlyLintPass for IncompleteFeatures { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { - let features = cx.sess.features_untracked(); + let features = cx.sess().features_untracked(); features .declared_lang_features .iter() @@ -2501,7 +2494,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { _ => {} } } - } else if let hir::ExprKind::MethodCall(_, _, ref args, _) = expr.kind { + } else if let hir::ExprKind::MethodCall(_, ref args, _) = expr.kind { // Find problematic calls to `MaybeUninit::assume_init`. let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { @@ -2840,7 +2833,7 @@ impl ClashingExternDeclarations { return true; } let tcx = cx.tcx; - if a == b || rustc_middle::ty::TyS::same_type(a, b) { + if a == b { // All nominally-same types are structurally same, too. true } else { @@ -2902,26 +2895,22 @@ impl ClashingExternDeclarations { } (Array(a_ty, a_const), Array(b_ty, b_const)) => { // For arrays, we also check the constness of the type. - a_const.val == b_const.val - && structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) + a_const.val() == b_const.val() + && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) } (Slice(a_ty), Slice(b_ty)) => { - structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) + structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) } (RawPtr(a_tymut), RawPtr(b_tymut)) => { a_tymut.mutbl == b_tymut.mutbl && structurally_same_type_impl( - seen_types, - cx, - &a_tymut.ty, - &b_tymut.ty, - ckind, + seen_types, cx, a_tymut.ty, b_tymut.ty, ckind, ) } (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { // For structural sameness, we don't need the region to be same. a_mut == b_mut - && structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) + && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) } (FnDef(..), FnDef(..)) => { let a_poly_sig = a.fn_sig(tcx); @@ -2934,7 +2923,7 @@ impl ClashingExternDeclarations { (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { - structurally_same_type_impl(seen_types, cx, a, b, ckind) + structurally_same_type_impl(seen_types, cx, *a, *b, ckind) }) && structurally_same_type_impl( seen_types, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d93866443a..d2d853efda 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -16,10 +16,9 @@ use self::TargetLint::*; -use crate::levels::{is_known_lint_tool, LintLevelsBuilder}; +use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; -use ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; -use rustc_ast as ast; +use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync; use rustc_errors::{struct_span_err, Applicability, SuggestionStyle}; @@ -32,13 +31,14 @@ use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; +use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt}; use rustc_serialize::json::Json; use rustc_session::lint::{BuiltinLintDiagnostics, ExternDepSpec}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; use rustc_session::Session; use rustc_span::lev_distance::find_best_match_for_name; -use rustc_span::{symbol::Symbol, BytePos, MultiSpan, Span, DUMMY_SP}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_target::abi; use tracing::debug; @@ -143,7 +143,11 @@ impl LintStore { &self.lints } - pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec, bool)> { + pub fn get_lint_groups<'t>( + &'t self, + ) -> impl Iterator, bool)> + 't { + // This function is not used in a way which observes the order of lints. + #[cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] self.lint_groups .iter() .filter(|(_, LintGroup { depr, .. })| { @@ -153,7 +157,6 @@ impl LintStore { .map(|(k, LintGroup { lint_ids, from_plugin, .. })| { (*k, lint_ids.clone(), *from_plugin) }) - .collect() } pub fn register_early_pass( @@ -313,7 +316,7 @@ impl LintStore { sess: &Session, lint_name: &str, level: Level, - crate_attrs: &[ast::Attribute], + registered_tools: &RegisteredTools, ) { let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name); if lint_name_only == crate::WARNINGS.name_lower() && level == Level::ForceWarn { @@ -326,7 +329,7 @@ impl LintStore { ) .emit(); } - let db = match self.check_lint_name(sess, lint_name_only, tool_name, crate_attrs) { + let db = match self.check_lint_name(lint_name_only, tool_name, registered_tools) { CheckLintNameResult::Ok(_) => None, CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)), CheckLintNameResult::NoLint(suggestion) => { @@ -397,13 +400,16 @@ impl LintStore { /// printing duplicate warnings. pub fn check_lint_name( &self, - sess: &Session, lint_name: &str, tool_name: Option, - crate_attrs: &[ast::Attribute], + registered_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { if let Some(tool_name) = tool_name { - if !is_known_lint_tool(tool_name, sess, crate_attrs) { + // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes. + if tool_name != sym::rustc + && tool_name != sym::rustdoc + && !registered_tools.contains(&Ident::with_dummy_span(tool_name)) + { return CheckLintNameResult::NoTool; } } @@ -521,7 +527,7 @@ impl LintStore { } } -/// Context for lint checking after type checking. +/// Context for lint checking outside of type inference. pub struct LateContext<'tcx> { /// Type context we're checking in. pub tcx: TyCtxt<'tcx>, @@ -553,20 +559,9 @@ pub struct LateContext<'tcx> { pub only_module: bool, } -/// Context for lint checking of the AST, after expansion, before lowering to -/// HIR. +/// Context for lint checking of the AST, after expansion, before lowering to HIR. pub struct EarlyContext<'a> { - /// Type context we're checking in. - pub sess: &'a Session, - - /// The crate being checked. - pub krate: &'a ast::Crate, - pub builder: LintLevelsBuilder<'a>, - - /// The store of registered lints and the lint levels. - pub lint_store: &'a LintStore, - pub buffered: LintBuffer, } @@ -802,19 +797,20 @@ pub trait LintContext: Sized { } impl<'a> EarlyContext<'a> { - pub fn new( + pub(crate) fn new( sess: &'a Session, + warn_about_weird_lints: bool, lint_store: &'a LintStore, - krate: &'a ast::Crate, - crate_attrs: &'a [ast::Attribute], + registered_tools: &'a RegisteredTools, buffered: LintBuffer, - warn_about_weird_lints: bool, ) -> EarlyContext<'a> { EarlyContext { - sess, - krate, - lint_store, - builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store, crate_attrs), + builder: LintLevelsBuilder::new( + sess, + warn_about_weird_lints, + lint_store, + registered_tools, + ), buffered, } } @@ -852,11 +848,11 @@ impl LintContext for EarlyContext<'_> { /// Gets the overall compiler `Session` object. fn sess(&self) -> &Session { - &self.sess + &self.builder.sess() } fn lints(&self) -> &LintStore { - &*self.lint_store + self.builder.lint_store() } fn lookup>( @@ -978,7 +974,7 @@ impl<'tcx> LateContext<'tcx> { Ok(()) } - fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result { + fn print_const(self, _ct: ty::Const<'tcx>) -> Result { Ok(()) } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 0bba66d383..1b2c88867d 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -16,9 +16,11 @@ use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; -use rustc_ast as ast; -use rustc_ast::visit as ast_visit; +use rustc_ast::ptr::P; +use rustc_ast::visit::{self as ast_visit, Visitor}; use rustc_ast::AstLike; +use rustc_ast::{self as ast, walk_list}; +use rustc_middle::ty::RegisteredTools; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; use rustc_span::symbol::Ident; @@ -31,7 +33,7 @@ macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.pass.$f(&$cx.context, $($args),*); }) } -struct EarlyContextAndPass<'a, T: EarlyLintPass> { +pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { context: EarlyContext<'a>, pass: T, } @@ -57,7 +59,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { F: FnOnce(&mut Self), { let is_crate_node = id == ast::CRATE_NODE_ID; - let push = self.context.builder.push(attrs, &self.context.lint_store, is_crate_node); + let push = self.context.builder.push(attrs, is_crate_node); self.check_id(id); self.enter_attrs(attrs); f(self); @@ -325,48 +327,89 @@ macro_rules! early_lint_pass_impl { crate::early_lint_methods!(early_lint_pass_impl, []); -fn early_lint_crate( +/// Early lints work on different nodes - either on the crate root, or on freshly loaded modules. +/// This trait generalizes over those nodes. +pub trait EarlyCheckNode<'a>: Copy { + fn id(self) -> ast::NodeId; + fn attrs<'b>(self) -> &'b [ast::Attribute] + where + 'a: 'b; + fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + where + 'a: 'b; +} + +impl<'a> EarlyCheckNode<'a> for &'a ast::Crate { + fn id(self) -> ast::NodeId { + ast::CRATE_NODE_ID + } + fn attrs<'b>(self) -> &'b [ast::Attribute] + where + 'a: 'b, + { + &self.attrs + } + fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + where + 'a: 'b, + { + run_early_pass!(cx, check_crate, self); + ast_visit::walk_crate(cx, self); + run_early_pass!(cx, check_crate_post, self); + } +} + +impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P]) { + fn id(self) -> ast::NodeId { + self.0 + } + fn attrs<'b>(self) -> &'b [ast::Attribute] + where + 'a: 'b, + { + self.1 + } + fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + where + 'a: 'b, + { + walk_list!(cx, visit_attribute, self.1); + walk_list!(cx, visit_item, self.2); + } +} + +fn early_lint_node<'a>( sess: &Session, + warn_about_weird_lints: bool, lint_store: &LintStore, - krate: &ast::Crate, - crate_attrs: &[ast::Attribute], - pass: T, + registered_tools: &RegisteredTools, buffered: LintBuffer, - warn_about_weird_lints: bool, + pass: impl EarlyLintPass, + check_node: impl EarlyCheckNode<'a>, ) -> LintBuffer { let mut cx = EarlyContextAndPass { context: EarlyContext::new( sess, + warn_about_weird_lints, lint_store, - krate, - crate_attrs, + registered_tools, buffered, - warn_about_weird_lints, ), pass, }; - // Visit the whole crate. - cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| { - // since the root module isn't visited as an item (because it isn't an - // item), warn for it here. - run_early_pass!(cx, check_crate, krate); - - ast_visit::walk_crate(cx, krate); - - run_early_pass!(cx, check_crate_post, krate); - }); + cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); cx.context.buffered } -pub fn check_ast_crate( +pub fn check_ast_node<'a>( sess: &Session, - lint_store: &LintStore, - krate: &ast::Crate, - crate_attrs: &[ast::Attribute], pre_expansion: bool, + lint_store: &LintStore, + registered_tools: &RegisteredTools, lint_buffer: Option, - builtin_lints: T, + builtin_lints: impl EarlyLintPass, + check_node: impl EarlyCheckNode<'a>, ) { let passes = if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; @@ -374,39 +417,39 @@ pub fn check_ast_crate( let mut buffered = lint_buffer.unwrap_or_default(); if !sess.opts.debugging_opts.no_interleave_lints { - buffered = early_lint_crate( + buffered = early_lint_node( sess, + pre_expansion, lint_store, - krate, - crate_attrs, - builtin_lints, + registered_tools, buffered, - pre_expansion, + builtin_lints, + check_node, ); if !passes.is_empty() { - buffered = early_lint_crate( + buffered = early_lint_node( sess, + false, lint_store, - krate, - crate_attrs, - EarlyLintPassObjects { lints: &mut passes[..] }, + registered_tools, buffered, - false, + EarlyLintPassObjects { lints: &mut passes[..] }, + check_node, ); } } else { for (i, pass) in passes.iter_mut().enumerate() { buffered = sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| { - early_lint_crate( + early_lint_node( sess, + pre_expansion && i == 0, lint_store, - krate, - crate_attrs, - EarlyLintPassObjects { lints: slice::from_mut(pass) }, + registered_tools, buffered, - pre_expansion && i == 0, + EarlyLintPassObjects { lints: slice::from_mut(pass) }, + check_node, ) }); } diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index 65772d0237..c5e15a88fd 100644 --- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs +++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs @@ -38,7 +38,7 @@ declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]); /// Returns `true` if we know for sure that the given type is not an enum. Note that for cases where /// the type is generic, we can't be certain if it will be an enum so we have to assume that it is. fn is_non_enum(t: Ty<'_>) -> bool { - !t.is_enum() && !t.potentially_needs_subst() + !t.is_enum() && !t.needs_subst() } fn enforce_mem_discriminant( diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index c64a67b6b9..944a099642 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -5,10 +5,8 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext} use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{ - GenericArg, HirId, Item, ItemKind, MutTy, Mutability, Node, Path, PathSegment, QPath, Ty, - TyKind, -}; +use rustc_hir::{Expr, ExprKind, GenericArg, Path, PathSegment, QPath}; +use rustc_hir::{HirId, Item, ItemKind, Node, Ty, TyKind}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -52,16 +50,66 @@ impl LateLintPass<'_> for DefaultHashTypes { } declare_tool_lint! { - pub rustc::USAGE_OF_TY_TYKIND, + pub rustc::POTENTIAL_QUERY_INSTABILITY, Allow, - "usage of `ty::TyKind` outside of the `ty::sty` module", + "require explicit opt-in when using potentially unstable methods or functions", report_in_external_macro: true } +declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY]); + +impl LateLintPass<'_> for QueryStability { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + // FIXME(rustdoc): This lint uses typecheck results, causing rustdoc to + // error if there are resolution failures. + // + // As internal lints are currently always run if there are `unstable_options`, + // they are added to the lint store of rustdoc. Internal lints are also + // not used via the `lint_mod` query. Crate lints run outside of a query + // so rustdoc currently doesn't disable them. + // + // Instead of relying on this, either change crate lints to a query disabled by + // rustdoc, only run internal lints if the user is explicitly opting in + // or figure out a different way to avoid running lints for rustdoc. + if cx.tcx.sess.opts.actually_rustdoc { + return; + } + + let (span, def_id, substs) = match expr.kind { + ExprKind::MethodCall(segment, _, _) + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => + { + (segment.ident.span, def_id, cx.typeck_results().node_substs(expr.hir_id)) + }, + _ => { + let &ty::FnDef(def_id, substs) = + cx.typeck_results() + .node_type(expr.hir_id) + .kind() else { return }; + (expr.span, def_id, substs) + } + }; + if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) { + let def_id = instance.def_id(); + if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { + cx.struct_span_lint(POTENTIAL_QUERY_INSTABILITY, span, |lint| { + let msg = format!( + "using `{}` can result in unstable query results", + cx.tcx.item_name(def_id) + ); + lint.build(&msg) + .note("if you believe this case to be fine, allow this lint and add a comment explaining your rationale") + .emit(); + }) + } + } + } +} + declare_tool_lint! { - pub rustc::TY_PASS_BY_REFERENCE, + pub rustc::USAGE_OF_TY_TYKIND, Allow, - "passing `Ty` or `TyCtxt` by reference", + "usage of `ty::TyKind` outside of the `ty::sty` module", report_in_external_macro: true } @@ -74,7 +122,6 @@ declare_tool_lint! { declare_lint_pass!(TyTyKind => [ USAGE_OF_TY_TYKIND, - TY_PASS_BY_REFERENCE, USAGE_OF_QUALIFIED_TY, ]); @@ -119,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { lint.build(&format!("usage of qualified `ty::{}`", t)) .span_suggestion( path.span, - "try using it unqualified", + "try importing it and using it unqualified", t, // The import probably needs to be changed Applicability::MaybeIncorrect, @@ -131,26 +178,6 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } } } - TyKind::Rptr(_, MutTy { ty: inner_ty, mutbl: Mutability::Not }) => { - if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { - if cx.tcx.impl_trait_ref(impl_did).is_some() { - return; - } - } - if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) { - cx.struct_span_lint(TY_PASS_BY_REFERENCE, ty.span, |lint| { - lint.build(&format!("passing `{}` by reference", t)) - .span_suggestion( - ty.span, - "try passing by value", - t, - // Changing type of function argument - Applicability::MaybeIncorrect, - ) - .emit(); - }) - } - } _ => {} } } @@ -175,7 +202,7 @@ fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, ty: &Ty<'_>) -> Option { } } // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait. - Res::SelfTy(None, Some((did, _))) => { + Res::SelfTy { trait_: None, alias_to: Some((did, _)) } => { if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() { if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did) diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 773e5751f1..0ce760b64d 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -21,7 +21,7 @@ use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit as hir_visit; use rustc_hir::intravisit::Visitor; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::LintPass; use rustc_span::symbol::Symbol; @@ -94,13 +94,13 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { } impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; /// Because lints are scoped lexically, we want to walk nested /// items in the context of the outer item, so enable /// deep-walking. - fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { - hir_visit::NestedVisitorMap::All(self.context.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.context.tcx.hir() } fn visit_nested_body(&mut self, body_id: hir::BodyId) { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index d3fa08650d..8afbd462c1 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -5,8 +5,8 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::{intravisit, HirId, CRATE_HIR_ID}; -use rustc_middle::hir::map::Map; +use rustc_hir::{intravisit, HirId}; +use rustc_middle::hir::nested_filter; use rustc_middle::lint::LevelAndSource; use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::lint::{ @@ -14,7 +14,7 @@ use rustc_middle::lint::{ COMMAND_LINE, }; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS}, Level, Lint, LintId, @@ -27,14 +27,14 @@ use tracing::debug; fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { let store = unerased_lint_store(tcx); - let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); - let levels = LintLevelsBuilder::new(tcx.sess, false, &store, crate_attrs); - let mut builder = LintLevelMapBuilder { levels, tcx, store }; + let levels = + LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools); + let mut builder = LintLevelMapBuilder { levels, tcx }; let krate = tcx.hir().krate(); builder.levels.id_to_set.reserve(krate.owners.len() + 1); - let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), &store, true); + let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true); builder.levels.register_id(hir::CRATE_HIR_ID); tcx.hir().walk_toplevel_module(&mut builder); builder.levels.pop(push); @@ -49,7 +49,7 @@ pub struct LintLevelsBuilder<'s> { cur: LintStackIndex, warn_about_weird_lints: bool, store: &'s LintStore, - crate_attrs: &'s [ast::Attribute], + registered_tools: &'s RegisteredTools, } pub struct BuilderPush { @@ -62,7 +62,7 @@ impl<'s> LintLevelsBuilder<'s> { sess: &'s Session, warn_about_weird_lints: bool, store: &'s LintStore, - crate_attrs: &'s [ast::Attribute], + registered_tools: &'s RegisteredTools, ) -> Self { let mut builder = LintLevelsBuilder { sess, @@ -71,19 +71,27 @@ impl<'s> LintLevelsBuilder<'s> { id_to_set: Default::default(), warn_about_weird_lints, store, - crate_attrs, + registered_tools, }; builder.process_command_line(sess, store); assert_eq!(builder.sets.list.len(), 1); builder } + pub(crate) fn sess(&self) -> &Session { + self.sess + } + + pub(crate) fn lint_store(&self) -> &LintStore { + self.store + } + fn process_command_line(&mut self, sess: &Session, store: &LintStore) { let mut specs = FxHashMap::default(); self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid); for &(ref lint_name, level) in &sess.opts.lint_opts { - store.check_lint_name_cmdline(sess, &lint_name, level, self.crate_attrs); + store.check_lint_name_cmdline(sess, &lint_name, level, self.registered_tools); let orig_level = level; let lint_flag_val = Symbol::intern(lint_name); @@ -217,12 +225,7 @@ impl<'s> LintLevelsBuilder<'s> { /// `#[allow]` /// /// Don't forget to call `pop`! - pub(crate) fn push( - &mut self, - attrs: &[ast::Attribute], - store: &LintStore, - is_crate_node: bool, - ) -> BuilderPush { + pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush { let mut specs = FxHashMap::default(); let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); @@ -310,7 +313,8 @@ impl<'s> LintLevelsBuilder<'s> { }; let tool_name = tool_ident.map(|ident| ident.name); let name = pprust::path_to_string(&meta_item.path); - let lint_result = store.check_lint_name(sess, &name, tool_name, self.crate_attrs); + let lint_result = + self.store.check_lint_name(&name, tool_name, self.registered_tools); match &lint_result { CheckLintNameResult::Ok(ids) => { let src = LintLevelSource::Node( @@ -459,7 +463,7 @@ impl<'s> LintLevelsBuilder<'s> { // Ignore any errors or warnings that happen because the new name is inaccurate // NOTE: `new_name` already includes the tool name, so we don't have to add it again. if let CheckLintNameResult::Ok(ids) = - store.check_lint_name(sess, &new_name, None, self.crate_attrs) + self.store.check_lint_name(&new_name, None, self.registered_tools) { let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason); for &id in ids { @@ -562,34 +566,19 @@ impl<'s> LintLevelsBuilder<'s> { } } -pub fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool { - if [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item) { - return true; - } - // Look for registered tools - // NOTE: does no error handling; error handling is done by rustc_resolve. - sess.filter_by_name(attrs, sym::register_tool) - .filter_map(|attr| attr.meta_item_list()) - .flatten() - .filter_map(|nested_meta| nested_meta.ident()) - .map(|ident| ident.name) - .any(|name| name == m_item) -} - -struct LintLevelMapBuilder<'a, 'tcx> { +struct LintLevelMapBuilder<'tcx> { levels: LintLevelsBuilder<'tcx>, tcx: TyCtxt<'tcx>, - store: &'a LintStore, } -impl LintLevelMapBuilder<'_, '_> { +impl LintLevelMapBuilder<'_> { fn with_lint_attrs(&mut self, id: hir::HirId, f: F) where F: FnOnce(&mut Self), { let is_crate_hir = id == hir::CRATE_HIR_ID; let attrs = self.tcx.hir().attrs(id); - let push = self.levels.push(attrs, self.store, is_crate_hir); + let push = self.levels.push(attrs, is_crate_hir); if push.changed { self.levels.register_id(id); } @@ -598,11 +587,11 @@ impl LintLevelMapBuilder<'_, '_> { } } -impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> { - type Map = Map<'tcx>; +impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c7823032b0..69863b5ff8 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -30,12 +30,14 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(crate_visibility_modifier)] +#![feature(if_let_guard)] #![feature(iter_order_by)] #![feature(let_else)] #![feature(never_type)] #![feature(nll)] #![feature(control_flow_enum)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_middle; @@ -56,6 +58,7 @@ mod non_ascii_idents; mod non_fmt_panic; mod nonstandard_style; mod noop_method_call; +mod pass_by_value; mod passes; mod redundant_semicolon; mod traits; @@ -85,6 +88,7 @@ use non_ascii_idents::*; use non_fmt_panic::NonPanicFmt; use nonstandard_style::*; use noop_method_call::*; +use pass_by_value::*; use redundant_semicolon::*; use traits::*; use types::*; @@ -92,8 +96,9 @@ use unused::*; /// Useful for other parts of the compiler / Clippy. pub use builtin::SoftLints; -pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; -pub use early::check_ast_crate; +pub use context::{CheckLintNameResult, FindLintError, LintStore}; +pub use context::{EarlyContext, LateContext, LintContext}; +pub use early::{check_ast_node, EarlyCheckNode}; pub use late::check_crate; pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_session::lint::Level::{self, *}; @@ -478,6 +483,11 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { for more information", ); store.register_removed("plugin_as_library", "plugins have been deprecated and retired"); + store.register_removed( + "unsupported_naked_functions", + "converted into hard error, see RFC 2972 \ + for more information", + ); } fn register_internals(store: &mut LintStore) { @@ -485,19 +495,24 @@ fn register_internals(store: &mut LintStore) { store.register_early_pass(|| Box::new(LintPassImpl)); store.register_lints(&DefaultHashTypes::get_lints()); store.register_late_pass(|| Box::new(DefaultHashTypes)); + store.register_lints(&QueryStability::get_lints()); + store.register_late_pass(|| Box::new(QueryStability)); store.register_lints(&ExistingDocKeyword::get_lints()); store.register_late_pass(|| Box::new(ExistingDocKeyword)); store.register_lints(&TyTyKind::get_lints()); store.register_late_pass(|| Box::new(TyTyKind)); + store.register_lints(&PassByValue::get_lints()); + store.register_late_pass(|| Box::new(PassByValue)); store.register_group( false, "rustc::internal", None, vec![ LintId::of(DEFAULT_HASH_TYPES), + LintId::of(POTENTIAL_QUERY_INSTABILITY), LintId::of(USAGE_OF_TY_TYKIND), + LintId::of(PASS_BY_VALUE), LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), - LintId::of(TY_PASS_BY_REFERENCE), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(EXISTING_DOC_KEYWORD), ], diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs index 5558947de0..ae93683475 100644 --- a/compiler/rustc_lint/src/methods.rs +++ b/compiler/rustc_lint/src/methods.rs @@ -44,7 +44,7 @@ fn in_macro(span: Span) -> bool { fn first_method_call<'tcx>( expr: &'tcx Expr<'tcx>, ) -> Option<(&'tcx PathSegment<'tcx>, &'tcx [Expr<'tcx>])> { - if let ExprKind::MethodCall(path, _, args, _) = &expr.kind { + if let ExprKind::MethodCall(path, args, _) = &expr.kind { if args.iter().any(|e| e.span.from_expansion()) { None } else { Some((path, *args)) } } else { None diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index a570206f1e..2dd6dbd67a 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -166,7 +166,7 @@ impl EarlyLintPass for NonAsciiIdents { } let mut has_non_ascii_idents = false; - let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock(); + let symbols = cx.sess().parse_sess.symbol_gallery.symbols.lock(); // Sort by `Span` so that error messages make sense with respect to the // order of identifier locations in the code. diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index f2fee67115..3130d57c2a 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -355,5 +355,5 @@ fn is_arg_inside_call(arg: Span, call: Span) -> bool { // panic call in the source file, to avoid invalid suggestions when macros are involved. // We specifically check for the spans to not be identical, as that happens sometimes when // proc_macros lie about spans and apply the same span to all the tokens they produce. - call.contains(arg) && !call.source_equal(&arg) + call.contains(arg) && !call.source_equal(arg) } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index be7756b0f2..f73388c675 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -164,7 +164,7 @@ impl EarlyLintPass for NonCamelCaseTypes { let has_repr_c = it .attrs .iter() - .any(|attr| attr::find_repr_attrs(&cx.sess, attr).contains(&attr::ReprC)); + .any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC)); if has_repr_c { return; diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 600504f7c1..39b5b7afda 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -40,7 +40,7 @@ declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // We only care about method calls. - let ExprKind::MethodCall(call, _, elements, _) = &expr.kind else { + let ExprKind::MethodCall(call, elements, _) = &expr.kind else { return }; // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow` @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { _ => return, }; let substs = cx.typeck_results().node_substs(expr.hir_id); - if substs.definitely_needs_subst(cx.tcx) { + if substs.needs_subst() { // We can't resolve on types that require monomorphization, so we don't handle them if // we need to perfom substitution. return; diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs new file mode 100644 index 0000000000..c47fdc063a --- /dev/null +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -0,0 +1,95 @@ +use crate::{LateContext, LateLintPass, LintContext}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +declare_tool_lint! { + /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to + /// always be passed by value. This is usually used for types that are thin wrappers around + /// references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which + /// is a reference to an `Interned`) + pub rustc::PASS_BY_VALUE, + Warn, + "pass by reference of a type flagged as `#[rustc_pass_by_value]`", + report_in_external_macro: true +} + +declare_lint_pass!(PassByValue => [PASS_BY_VALUE]); + +impl<'tcx> LateLintPass<'tcx> for PassByValue { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { + match &ty.kind { + TyKind::Rptr(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { + if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { + if cx.tcx.impl_trait_ref(impl_did).is_some() { + return; + } + } + if let Some(t) = path_for_pass_by_value(cx, &inner_ty) { + cx.struct_span_lint(PASS_BY_VALUE, ty.span, |lint| { + lint.build(&format!("passing `{}` by reference", t)) + .span_suggestion( + ty.span, + "try passing by value", + t, + // Changing type of function argument + Applicability::MaybeIncorrect, + ) + .emit(); + }) + } + } + _ => {} + } + } +} + +fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option { + if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind { + match path.res { + Res::Def(_, def_id) if cx.tcx.has_attr(def_id, sym::rustc_pass_by_value) => { + let name = cx.tcx.item_name(def_id).to_ident_string(); + let path_segment = path.segments.last().unwrap(); + return Some(format!("{}{}", name, gen_args(cx, path_segment))); + } + Res::SelfTy { trait_: None, alias_to: Some((did, _)) } => { + if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() { + if cx.tcx.has_attr(adt.did, sym::rustc_pass_by_value) { + return Some(cx.tcx.def_path_str_with_substs(adt.did, substs)); + } + } + } + _ => (), + } + } + + None +} + +fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String { + if let Some(args) = &segment.args { + let params = args + .args + .iter() + .map(|arg| match arg { + GenericArg::Lifetime(lt) => lt.name.ident().to_string(), + GenericArg::Type(ty) => { + cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into()) + } + GenericArg::Const(c) => { + cx.tcx.sess.source_map().span_to_snippet(c.span).unwrap_or_else(|_| "_".into()) + } + GenericArg::Infer(_) => String::from("_"), + }) + .collect::>(); + + if !params.is_empty() { + return format!("<{}>", params.join(", ")); + } + } + + String::new() +} diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index dafff640b3..4c7f348277 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -86,7 +86,6 @@ 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; use rustc_middle::ty::PredicateKind::*; let predicates = cx.tcx.explicit_predicates_of(item.def_id); @@ -94,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { let Trait(trait_predicate) = predicate.kind().skip_binder() else { continue }; - if trait_predicate.constness == ty::BoundConstness::ConstIfConst { + if trait_predicate.is_const_if_const() { // `~const Drop` definitely have meanings so avoid linting here. continue; } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 32ed6dad7f..fc88e8cd91 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -249,7 +249,7 @@ fn report_bin_hex_error( )); } if let Some(sugg_ty) = - get_type_suggestion(&cx.typeck_results().node_type(expr.hir_id), val, negative) + get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative) { if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { let (sans_suffix, _) = repr_str.split_at(pos); @@ -367,7 +367,7 @@ fn lint_int_literal<'tcx>( max, )); if let Some(sugg_ty) = - get_type_suggestion(&cx.typeck_results().node_type(e.hir_id), v, negative) + get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) { err.help(&format!("consider using the type `{}` instead", sugg_ty)); } @@ -1095,7 +1095,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } for arg in sig.inputs() { - let r = self.check_type_for_ffi(cache, arg); + let r = self.check_type_for_ffi(cache, *arg); match r { FfiSafe => {} _ => { @@ -1175,9 +1175,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { type BreakTy = Ty<'tcx>; - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.cx.tcx) - } fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { match ty.kind() { @@ -1260,7 +1257,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let sig = self.cx.tcx.erase_late_bound_regions(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, false, false); + self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false); } if let hir::FnRetTy::Return(ref ret_hir) = decl.output { @@ -1467,7 +1464,7 @@ impl InvalidAtomicOrdering { sym::AtomicI128, ]; if_chain! { - if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(ref method_path, args, _) = &expr.kind; if recognized_names.contains(&method_path.ident.name); if let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_did) = cx.tcx.impl_of_method(m_def_id); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 6b89fd657b..adec1a3ab0 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1793,6 +1793,10 @@ declare_lint! { Warn, "detects name collision with an existing but unstable method", @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::Custom( + "once this associated item is added to the standard library, \ + the ambiguity may cause an error or change in behavior!" + ), reference: "issue #48919 ", // Note: this item represents future incompatibility of all unstable functions in the // standard library, and thus should never be removed or changed to an error. @@ -1805,7 +1809,7 @@ declare_lint! { /// /// ### Example /// - /// ``` + /// ```rust /// if let _ = 123 { /// println!("always runs!"); /// } @@ -2335,6 +2339,10 @@ declare_lint! { Warn, "reservation of a two-phased borrow conflicts with other shared borrows", @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::Custom( + "this borrowing pattern was not meant to be accepted, \ + and may become a hard error in the future" + ), reference: "issue #59159 ", }; } @@ -2431,7 +2439,19 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: formatting may not be suitable for sub-register argument + /// --> src/main.rs:7:19 + /// | + /// 7 | asm!("mov {0}, {0}", in(reg) 0i16); + /// | ^^^ ^^^ ---- for this argument + /// | + /// = note: `#[warn(asm_sub_register)]` on by default + /// = help: use the `x` modifier to have the register formatted as `ax` + /// = help: or use the `r` modifier to keep the default formatting of `rax` + /// ``` /// /// ### Explanation /// @@ -2474,7 +2494,17 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + /// --> src/main.rs:8:14 + /// | + /// 8 | ".att_syntax", + /// | ^^^^^^^^^^^ + /// | + /// = note: `#[warn(bad_asm_style)]` on by default + /// ``` /// /// ### Explanation /// @@ -2741,58 +2771,12 @@ declare_lint! { "undefined naked function ABI" } -declare_lint! { - /// The `unsupported_naked_functions` lint detects naked function - /// definitions that are unsupported but were previously accepted. - /// - /// ### Example - /// - /// ```rust - /// #![feature(naked_functions)] - /// - /// #[naked] - /// pub extern "C" fn f() -> u32 { - /// 42 - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The naked functions must be defined using a single inline assembly - /// block. - /// - /// The execution must never fall through past the end of the assembly - /// code so the block must use `noreturn` option. The asm block can also - /// use `att_syntax` option, but other options are not allowed. - /// - /// The asm block must not contain any operands other than `const` and - /// `sym`. Additionally, naked function should specify a non-Rust ABI. - /// - /// Naked functions cannot be inlined. All forms of the `inline` attribute - /// are prohibited. - /// - /// While other definitions of naked functions were previously accepted, - /// they are unsupported and might not work reliably. This is a - /// [future-incompatible] lint that will transition into hard error in - /// the future. - /// - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub UNSUPPORTED_NAKED_FUNCTIONS, - Warn, - "unsupported naked function definitions", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #32408 ", - }; -} - declare_lint! { /// The `ineffective_unstable_trait_impl` lint detects `#[unstable]` attributes which are not used. /// /// ### Example /// - /// ```compile_fail + /// ```rust,compile_fail /// #![feature(staged_api)] /// /// #[derive(Clone)] @@ -2973,6 +2957,43 @@ declare_lint! { }; } +declare_lint! { + /// The `unexpected_cfgs` lint detects unexpected conditional compilation conditions. + /// + /// ### Example + /// + /// ```text + /// rustc --check-cfg 'names()' + /// ``` + /// + /// ```rust,ignore (needs command line option) + /// #[cfg(widnows)] + /// fn foo() {} + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: unknown condition name used + /// --> lint_example.rs:1:7 + /// | + /// 1 | #[cfg(widnows)] + /// | ^^^^^^^ + /// | + /// = note: `#[warn(unexpected_cfgs)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// This lint is only active when a `--check-cfg='names(...)'` option has been passed + /// to the compiler and triggers whenever an unknown condition name or value is used. + /// The known condition include names or values passed in `--check-cfg`, `--cfg`, and some + /// well-knows names and values built into the compiler. + pub UNEXPECTED_CFGS, + Warn, + "detects unexpected names and values in `#[cfg]` conditions", +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -3052,7 +3073,6 @@ declare_lint_pass! { UNINHABITED_STATIC, FUNCTION_ITEM_REFERENCES, USELESS_DEPRECATED, - UNSUPPORTED_NAKED_FUNCTIONS, MISSING_ABI, INVALID_DOC_ATTRIBUTES, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, @@ -3071,6 +3091,8 @@ declare_lint_pass! { DEREF_INTO_DYN_SUPERTRAIT, DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DUPLICATE_MACRO_ATTRIBUTES, + SUSPICIOUS_AUTO_TRAIT_IMPLS, + UNEXPECTED_CFGS, ] } @@ -3622,7 +3644,17 @@ declare_lint! { /// fn foo() {} /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// warning: duplicated attribute + /// --> src/lib.rs:2:1 + /// | + /// 2 | #[test] + /// | ^^^^^^^ + /// | + /// = note: `#[warn(duplicate_macro_attributes)]` on by default + /// ``` /// /// ### Explanation /// @@ -3637,3 +3669,37 @@ declare_lint! { Warn, "duplicated attribute" } + +declare_lint! { + /// The `suspicious_auto_trait_impls` lint checks for potentially incorrect + /// implementations of auto traits. + /// + /// ### Example + /// + /// ```rust + /// struct Foo(T); + /// + /// unsafe impl Send for Foo<*const T> {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A type can implement auto traits, e.g. `Send`, `Sync` and `Unpin`, + /// in two different ways: either by writing an explicit impl or if + /// all fields of the type implement that auto trait. + /// + /// The compiler disables the automatic implementation if an explicit one + /// exists for given type constructor. The exact rules governing this + /// are currently unsound and quite subtle and and will be modified in the future. + /// This change will cause the automatic implementation to be disabled in more + /// cases, potentially breaking some code. + pub SUSPICIOUS_AUTO_TRAIT_IMPLS, + Warn, + "the rules governing auto traits will change in the future", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, + reference: "issue #93367 ", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 97f6df51f8..1f834b7212 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -163,12 +163,17 @@ pub enum FutureIncompatibilityReason { /// This will be an error in a future release, and /// Cargo should create a report even for dependencies FutureReleaseErrorReportNow, + /// Code that changes meaning in some way in a + /// future release. + FutureReleaseSemanticsChange, /// Previously accepted code that will become an /// error in the provided edition EditionError(Edition), /// Code that changes meaning in some way in /// the provided edition EditionSemanticsChange(Edition), + /// A custom reason. + Custom(&'static str), } impl FutureIncompatibilityReason { @@ -282,7 +287,7 @@ pub enum ExternDepSpec { // This could be a closure, but then implementing derive trait // becomes hacky (and it gets allocated). -#[derive(PartialEq, Debug)] +#[derive(Debug)] pub enum BuiltinLintDiagnostics { Normal, AbsPathWithModule(Span), @@ -309,7 +314,6 @@ pub enum BuiltinLintDiagnostics { /// Lints that are buffered up early on in the `Session` before the /// `LintLevels` is calculated. -#[derive(PartialEq)] pub struct BufferedEarlyLint { /// The span of code that we are linting on. pub span: MultiSpan, @@ -336,9 +340,7 @@ pub struct LintBuffer { impl LintBuffer { pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { let arr = self.map.entry(early_lint.node_id).or_default(); - if !arr.contains(&early_lint) { - arr.push(early_lint); - } + arr.push(early_lint); } pub fn add_lint( diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index e2ce7da0e8..a2b0e9b4d2 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -82,6 +82,8 @@ enum LLVMRustAttribute { StackProtectReq = 30, StackProtectStrong = 31, StackProtect = 32, + NoUndef = 33, + SanitizeMemTag = 34, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index f06fc3edf5..7030fd5370 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1168,25 +1168,6 @@ extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols, passes.run(*unwrap(M)); } -extern "C" void LLVMRustMarkAllFunctionsNounwind(LLVMModuleRef M) { - for (Module::iterator GV = unwrap(M)->begin(), E = unwrap(M)->end(); GV != E; - ++GV) { - GV->setDoesNotThrow(); - Function *F = dyn_cast(GV); - if (F == nullptr) - continue; - - for (Function::iterator B = F->begin(), BE = F->end(); B != BE; ++B) { - for (BasicBlock::iterator I = B->begin(), IE = B->end(); I != IE; ++I) { - if (isa(I)) { - InvokeInst *CI = cast(I); - CI->setDoesNotThrow(); - } - } - } - } -} - extern "C" void LLVMRustSetDataLayoutFromTargetMachine(LLVMModuleRef Module, LLVMTargetMachineRef TMR) { diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index c21e4acbef..c8f31adbfd 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -76,6 +76,10 @@ extern "C" void LLVMRustInstallFatalErrorHandler() { install_fatal_error_handler(FatalErrorHandler); } +extern "C" void LLVMRustDisableSystemDialogsOnCrash() { + sys::DisableSystemDialogsOnCrash(); +} + extern "C" char *LLVMRustGetLastError(void) { char *Ret = LastError; LastError = nullptr; @@ -220,6 +224,10 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::StackProtectStrong; case StackProtect: return Attribute::StackProtect; + case NoUndef: + return Attribute::NoUndef; + case SanitizeMemTag: + return Attribute::SanitizeMemTag; } report_fatal_error("bad AttributeKind"); } @@ -328,6 +336,17 @@ extern "C" void LLVMRustAddStructRetAttr(LLVMValueRef Fn, unsigned Index, AddAttribute(F, Index, Attr); } +extern "C" void LLVMRustEmitUWTableAttr(LLVMValueRef Fn, bool Async) { + Function *F = unwrap(Fn); +#if LLVM_VERSION_LT(15, 0) + Attribute Attr = Attribute::get(F->getContext(), Attribute::UWTable); +#else + Attribute Attr = Attribute::getWithUWTableKind( + F->getContext(), Async ? UWTableKind::Async : UWTableKind::Sync); +#endif + AddAttribute(F, AttributeList::AttrIndex::FunctionIndex, Attr); +} + extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn, unsigned Index, const char *Name, @@ -722,9 +741,12 @@ extern "C" bool LLVMRustIsRustLLVM() { #endif } -extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name, - uint32_t Value) { - unwrap(M)->addModuleFlag(Module::Warning, Name, Value); +extern "C" void LLVMRustAddModuleFlag( + LLVMModuleRef M, + Module::ModFlagBehavior MergeBehavior, + const char *Name, + uint32_t Value) { + unwrap(M)->addModuleFlag(MergeBehavior, Name, Value); } extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) { diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 3351564299..6c5461505f 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -47,7 +47,7 @@ fn decodable_body( quote! { ::rustc_serialize::Decoder::read_struct( __decoder, - |__decoder| { ::std::result::Result::Ok(#construct) }, + |__decoder| { #construct }, ) } } @@ -57,7 +57,7 @@ fn decodable_body( .enumerate() .map(|(idx, vi)| { let construct = vi.construct(|field, index| decode_field(field, index, false)); - quote! { #idx => { ::std::result::Result::Ok(#construct) } } + quote! { #idx => { #construct } } }) .collect(); let names: TokenStream = variants @@ -82,8 +82,7 @@ fn decodable_body( |__decoder, __variant_idx| { match __variant_idx { #match_inner - _ => return ::std::result::Result::Err( - ::rustc_serialize::Decoder::error(__decoder, #message)), + _ => panic!(#message), } }) } @@ -95,9 +94,7 @@ fn decodable_body( s.bound_impl( quote!(::rustc_serialize::Decodable<#decoder_ty>), quote! { - fn decode( - __decoder: &mut #decoder_ty, - ) -> ::std::result::Result::Error> { + fn decode(__decoder: &mut #decoder_ty) -> Self { #decode_body } }, @@ -127,12 +124,7 @@ fn decode_field(field: &syn::Field, index: usize, is_struct: bool) -> proc_macro #__decoder, #opt_field_name #decode_inner_method) }; - quote! { - match #decode_call { - ::std::result::Result::Ok(__res) => __res, - ::std::result::Result::Err(__err) => return ::std::result::Result::Err(__err), - } - } + quote! { #decode_call } } pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 918c3b9daf..046245080d 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -9,6 +9,7 @@ #![feature(try_blocks)] #![feature(never_type)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] extern crate proc_macro; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 13ea089e24..550b22a2a3 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -223,7 +223,7 @@ use rustc_data_structures::sync::MetadataRef; use rustc_errors::{struct_span_err, FatalError}; use rustc_session::config::{self, CrateType}; use rustc_session::cstore::{CrateSource, MetadataLoader}; -use rustc_session::filesearch::{FileDoesntMatch, FileMatches, FileSearch}; +use rustc_session::filesearch::FileSearch; use rustc_session::search_paths::PathKind; use rustc_session::utils::CanonicalizedPath; use rustc_session::Session; @@ -371,15 +371,20 @@ impl<'a> CrateLocator<'a> { extra_prefix: &str, seen_paths: &mut FxHashSet, ) -> Result, CrateError> { - // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" - let dylib_prefix = format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix); - let rlib_prefix = format!("lib{}{}", self.crate_name, extra_prefix); + let rmeta_prefix = &format!("lib{}{}", self.crate_name, extra_prefix); + let rlib_prefix = rmeta_prefix; + let dylib_prefix = + &format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix); let staticlib_prefix = - format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix); + &format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix); + + let rmeta_suffix = ".rmeta"; + let rlib_suffix = ".rlib"; + let dylib_suffix = &self.target.dll_suffix; + let staticlib_suffix = &self.target.staticlib_suffix; let mut candidates: FxHashMap<_, (FxHashMap<_, _>, FxHashMap<_, _>, FxHashMap<_, _>)> = Default::default(); - let mut staticlibs = vec![]; // First, find all possible candidate rlibs and dylibs purely based on // the name of the files themselves. We're trying to match against an @@ -394,46 +399,50 @@ impl<'a> CrateLocator<'a> { // of the crate id (path/name/id). // // The goal of this step is to look at as little metadata as possible. - self.filesearch.search(|spf, kind| { - let file = match &spf.file_name_str { - None => return FileDoesntMatch, - Some(file) => file, - }; - let (hash, found_kind) = if file.starts_with(&rlib_prefix) && file.ends_with(".rlib") { - (&file[(rlib_prefix.len())..(file.len() - ".rlib".len())], CrateFlavor::Rlib) - } else if file.starts_with(&rlib_prefix) && file.ends_with(".rmeta") { - (&file[(rlib_prefix.len())..(file.len() - ".rmeta".len())], CrateFlavor::Rmeta) - } else if file.starts_with(&dylib_prefix) && file.ends_with(&self.target.dll_suffix) { - ( - &file[(dylib_prefix.len())..(file.len() - self.target.dll_suffix.len())], - CrateFlavor::Dylib, - ) - } else { - if file.starts_with(&staticlib_prefix) - && file.ends_with(&self.target.staticlib_suffix) - { - staticlibs - .push(CrateMismatch { path: spf.path.clone(), got: "static".to_string() }); - } - return FileDoesntMatch; - }; + // Unfortunately, the prefix-based matching sometimes is over-eager. + // E.g. if `rlib_suffix` is `libstd` it'll match the file + // `libstd_detect-8d6701fb958915ad.rlib` (incorrect) as well as + // `libstd-f3ab5b1dea981f17.rlib` (correct). But this is hard to avoid + // given that `extra_filename` comes from the `-C extra-filename` + // option and thus can be anything, and the incorrect match will be + // handled safely in `extract_one`. + for search_path in self.filesearch.search_paths() { + debug!("searching {}", search_path.dir.display()); + for spf in search_path.files.iter() { + debug!("testing {}", spf.path.display()); + + let f = &spf.file_name_str; + let (hash, kind) = if f.starts_with(rlib_prefix) && f.ends_with(rlib_suffix) { + (&f[rlib_prefix.len()..(f.len() - rlib_suffix.len())], CrateFlavor::Rlib) + } else if f.starts_with(rmeta_prefix) && f.ends_with(rmeta_suffix) { + (&f[rmeta_prefix.len()..(f.len() - rmeta_suffix.len())], CrateFlavor::Rmeta) + } else if f.starts_with(dylib_prefix) && f.ends_with(dylib_suffix) { + (&f[dylib_prefix.len()..(f.len() - dylib_suffix.len())], CrateFlavor::Dylib) + } else { + if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix) { + self.crate_rejections.via_kind.push(CrateMismatch { + path: spf.path.clone(), + got: "static".to_string(), + }); + } + continue; + }; - info!("lib candidate: {}", spf.path.display()); + info!("lib candidate: {}", spf.path.display()); - let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default(); - let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); - if seen_paths.contains(&path) { - return FileDoesntMatch; - }; - seen_paths.insert(path.clone()); - match found_kind { - CrateFlavor::Rlib => rlibs.insert(path, kind), - CrateFlavor::Rmeta => rmetas.insert(path, kind), - CrateFlavor::Dylib => dylibs.insert(path, kind), - }; - FileMatches - }); - self.crate_rejections.via_kind.extend(staticlibs); + let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default(); + let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); + if seen_paths.contains(&path) { + continue; + }; + seen_paths.insert(path.clone()); + match kind { + CrateFlavor::Rlib => rlibs.insert(path, search_path.kind), + CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind), + CrateFlavor::Dylib => dylibs.insert(path, search_path.kind), + }; + } + } // We have now collected all known libraries into a set of candidates // keyed of the filename hash listed. For each filename, we also have a diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 639d2e617c..88292a4422 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -274,11 +274,6 @@ impl Collector<'_> { span, "`#[link(...)]` with `kind = \"raw-dylib\"` only supported on Windows", ); - } else if !self.tcx.sess.target.options.is_like_msvc { - self.tcx.sess.span_warn( - span, - "`#[link(...)]` with `kind = \"raw-dylib\"` not supported on windows-gnu", - ); } if lib_name.as_str().contains('\0') { @@ -409,11 +404,13 @@ impl Collector<'_> { fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef) -> DllImport { let calling_convention = if self.tcx.sess.target.arch == "x86" { match abi { - Abi::C { .. } | Abi::Cdecl => DllCallingConvention::C, + Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C, Abi::Stdcall { .. } | Abi::System { .. } => { DllCallingConvention::Stdcall(self.i686_arg_list_size(item)) } - Abi::Fastcall => DllCallingConvention::Fastcall(self.i686_arg_list_size(item)), + Abi::Fastcall { .. } => { + DllCallingConvention::Fastcall(self.i686_arg_list_size(item)) + } // Vectorcall is intentionally not supported at this time. _ => { self.tcx.sess.span_fatal( @@ -424,7 +421,7 @@ impl Collector<'_> { } } else { match abi { - Abi::C { .. } | Abi::Win64 | Abi::System { .. } => DllCallingConvention::C, + Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C, _ => { self.tcx.sess.span_fatal( item.span, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bb9a58a0b6..66968c9ba5 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -21,7 +21,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_hir::lang_items; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::mir::{self, Body, Promoted}; @@ -120,7 +120,7 @@ crate struct CrateMetadata { /// How to link (or not link) this crate to the currently compiled crate. dep_kind: Lock, /// Filesystem location of this crate. - source: CrateSource, + source: Lrc, /// Whether or not this crate should be consider a private dependency /// for purposes of the 'exported_private_dependencies' lint private_dep: bool, @@ -218,40 +218,40 @@ impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a MetadataBlob, &'tcx Session) { } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for &'a CrateMetadataRef<'a> { +impl<'a, 'tcx> Metadata<'a, 'tcx> for CrateMetadataRef<'a> { #[inline] fn blob(self) -> &'a MetadataBlob { - &self.blob + &self.cdata.blob } #[inline] fn cdata(self) -> Option> { - Some(*self) + Some(self) } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadataRef<'a>, &'tcx Session) { +impl<'a, 'tcx> Metadata<'a, 'tcx> for (CrateMetadataRef<'a>, &'tcx Session) { #[inline] fn blob(self) -> &'a MetadataBlob { - &self.0.blob + &self.0.cdata.blob } #[inline] fn cdata(self) -> Option> { - Some(*self.0) + Some(self.0) } #[inline] fn sess(self) -> Option<&'tcx Session> { - Some(&self.1) + Some(self.1) } } -impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadataRef<'a>, TyCtxt<'tcx>) { +impl<'a, 'tcx> Metadata<'a, 'tcx> for (CrateMetadataRef<'a>, TyCtxt<'tcx>) { #[inline] fn blob(self) -> &'a MetadataBlob { - &self.0.blob + &self.0.cdata.blob } #[inline] fn cdata(self) -> Option> { - Some(*self.0) + Some(self.0) } #[inline] fn tcx(self) -> Option> { @@ -263,7 +263,7 @@ impl<'a, 'tcx, T: Decodable>> Lazy { fn decode>(self, metadata: M) -> T { let mut dcx = metadata.decoder(self.position.get()); dcx.lazy_state = LazyState::NodeStart(self.position); - T::decode(&mut dcx).unwrap() + T::decode(&mut dcx) } } @@ -274,7 +274,7 @@ impl<'a: 'x, 'tcx: 'x, 'x, T: Decodable>> Lazy<[T]> { ) -> impl ExactSizeIterator + Captures<'a> + Captures<'tcx> + 'x { let mut dcx = metadata.decoder(self.position.get()); dcx.lazy_state = LazyState::NodeStart(self.position); - (0..self.meta).map(move |_| T::decode(&mut dcx).unwrap()) + (0..self.meta).map(move |_| T::decode(&mut dcx)) } } @@ -300,23 +300,19 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> { if cnum == LOCAL_CRATE { self.cdata().cnum } else { self.cdata().cnum_map[cnum] } } - fn read_lazy_with_meta( - &mut self, - meta: T::Meta, - ) -> Result, ::Error> { - let min_size = T::min_size(meta); - let distance = self.read_usize()?; + fn read_lazy_with_meta(&mut self, meta: T::Meta) -> Lazy { + let distance = self.read_usize(); let position = match self.lazy_state { LazyState::NoNode => bug!("read_lazy_with_meta: outside of a metadata node"), LazyState::NodeStart(start) => { let start = start.get(); - assert!(distance + min_size <= start); - start - distance - min_size + assert!(distance <= start); + start - distance } - LazyState::Previous(last_min_end) => last_min_end.get() + distance, + LazyState::Previous(last_pos) => last_pos.get() + distance, }; - self.lazy_state = LazyState::Previous(NonZeroUsize::new(position + min_size).unwrap()); - Ok(Lazy::from_position_and_meta(NonZeroUsize::new(position).unwrap(), meta)) + self.lazy_state = LazyState::Previous(NonZeroUsize::new(position).unwrap()); + Lazy::from_position_and_meta(NonZeroUsize::new(position).unwrap(), meta) } #[inline] @@ -343,25 +339,21 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { self.opaque.position() } - fn cached_ty_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> + fn cached_ty_for_shorthand(&mut self, shorthand: usize, or_insert_with: F) -> Ty<'tcx> where - F: FnOnce(&mut Self) -> Result, Self::Error>, + F: FnOnce(&mut Self) -> Ty<'tcx>, { let tcx = self.tcx(); let key = ty::CReaderCacheKey { cnum: Some(self.cdata().cnum), pos: shorthand }; if let Some(&ty) = tcx.ty_rcache.borrow().get(&key) { - return Ok(ty); + return ty; } - let ty = or_insert_with(self)?; + let ty = or_insert_with(self); tcx.ty_rcache.borrow_mut().insert(key, ty); - Ok(ty) + ty } fn with_position(&mut self, pos: usize, f: F) -> R @@ -377,7 +369,7 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { r } - fn decode_alloc_id(&mut self) -> Result { + fn decode_alloc_id(&mut self) -> rustc_middle::mir::interpret::AllocId { if let Some(alloc_decoding_session) = self.alloc_decoding_session { alloc_decoding_session.decode_alloc_id(self) } else { @@ -387,48 +379,48 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { } impl<'a, 'tcx> Decodable> for CrateNum { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { - let cnum = CrateNum::from_u32(d.read_u32()?); - Ok(d.map_encoded_cnum_to_current(cnum)) + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> CrateNum { + let cnum = CrateNum::from_u32(d.read_u32()); + d.map_encoded_cnum_to_current(cnum) } } impl<'a, 'tcx> Decodable> for DefIndex { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { - Ok(DefIndex::from_u32(d.read_u32()?)) + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> DefIndex { + DefIndex::from_u32(d.read_u32()) } } impl<'a, 'tcx> Decodable> for ExpnIndex { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { - Ok(ExpnIndex::from_u32(d.read_u32()?)) + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> ExpnIndex { + ExpnIndex::from_u32(d.read_u32()) } } impl<'a, 'tcx> Decodable> for SyntaxContext { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> SyntaxContext { let cdata = decoder.cdata(); let sess = decoder.sess.unwrap(); let cname = cdata.root.name; rustc_span::hygiene::decode_syntax_context(decoder, &cdata.hygiene_context, |_, id| { debug!("SpecializedDecoder: decoding {}", id); - Ok(cdata + cdata .root .syntax_contexts - .get(&cdata, id) + .get(cdata, id) .unwrap_or_else(|| panic!("Missing SyntaxContext {:?} for crate {:?}", id, cname)) - .decode((&cdata, sess))) + .decode((cdata, sess)) }) } } impl<'a, 'tcx> Decodable> for ExpnId { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> ExpnId { let local_cdata = decoder.cdata(); let sess = decoder.sess.unwrap(); - let cnum = CrateNum::decode(decoder)?; - let index = u32::decode(decoder)?; + let cnum = CrateNum::decode(decoder); + let index = u32::decode(decoder); let expn_id = rustc_span::hygiene::decode_expn_id(cnum, index, |expn_id| { let ExpnId { krate: cnum, local_id: index } = expn_id; @@ -443,34 +435,34 @@ impl<'a, 'tcx> Decodable> for ExpnId { let expn_data = crate_data .root .expn_data - .get(&crate_data, index) + .get(crate_data, index) .unwrap() - .decode((&crate_data, sess)); + .decode((crate_data, sess)); let expn_hash = crate_data .root .expn_hashes - .get(&crate_data, index) + .get(crate_data, index) .unwrap() - .decode((&crate_data, sess)); + .decode((crate_data, sess)); (expn_data, expn_hash) }); - Ok(expn_id) + expn_id } } impl<'a, 'tcx> Decodable> for Span { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { - let ctxt = SyntaxContext::decode(decoder)?; - let tag = u8::decode(decoder)?; + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Span { + let ctxt = SyntaxContext::decode(decoder); + let tag = u8::decode(decoder); if tag == TAG_PARTIAL_SPAN { - return Ok(DUMMY_SP.with_ctxt(ctxt)); + return DUMMY_SP.with_ctxt(ctxt); } debug_assert!(tag == TAG_VALID_SPAN_LOCAL || tag == TAG_VALID_SPAN_FOREIGN); - let lo = BytePos::decode(decoder)?; - let len = BytePos::decode(decoder)?; + let lo = BytePos::decode(decoder); + let len = BytePos::decode(decoder); let hi = lo + len; let Some(sess) = decoder.sess else { @@ -513,7 +505,7 @@ impl<'a, 'tcx> Decodable> for Span { if decoder.cdata().root.is_proc_macro_crate() { // Decode `CrateNum` as u32 - using `CrateNum::decode` will ICE // since we don't have `cnum_map` populated. - let cnum = u32::decode(decoder)?; + let cnum = u32::decode(decoder); panic!( "Decoding of crate {:?} tried to access proc-macro dep {:?}", decoder.cdata().root.name, @@ -521,7 +513,7 @@ impl<'a, 'tcx> Decodable> for Span { ); } // tag is TAG_VALID_SPAN_FOREIGN, checked by `debug_assert` above - let cnum = CrateNum::decode(decoder)?; + let cnum = CrateNum::decode(decoder); debug!( "SpecializedDecoder::specialized_decode: loading source files from cnum {:?}", cnum @@ -583,18 +575,18 @@ impl<'a, 'tcx> Decodable> for Span { (hi + source_file.translated_source_file.start_pos) - source_file.original_start_pos; // Do not try to decode parent for foreign spans. - Ok(Span::new(lo, hi, ctxt, None)) + Span::new(lo, hi, ctxt, None) } } impl<'a, 'tcx> Decodable> for &'tcx [thir::abstract_const::Node<'tcx>] { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { ty::codec::RefDecodable::decode(d) } } impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self { ty::codec::RefDecodable::decode(d) } } @@ -602,7 +594,7 @@ impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx impl<'a, 'tcx, T: Decodable>> Decodable> for Lazy { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Self { decoder.read_lazy_with_meta(()) } } @@ -610,9 +602,9 @@ impl<'a, 'tcx, T: Decodable>> Decodable>> Decodable> for Lazy<[T]> { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { - let len = decoder.read_usize()?; - if len == 0 { Ok(Lazy::empty()) } else { decoder.read_lazy_with_meta(len) } + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Self { + let len = decoder.read_usize(); + if len == 0 { Lazy::empty() } else { decoder.read_lazy_with_meta(len) } } } @@ -621,8 +613,8 @@ impl<'a, 'tcx, I: Idx, T: Decodable>> Decodable: FixedSizeEncoding, { - fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { - let len = decoder.read_usize()?; + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Self { + let len = decoder.read_usize(); decoder.read_lazy_with_meta(len) } } @@ -707,7 +699,7 @@ impl CrateRoot<'_> { } impl<'a, 'tcx> CrateMetadataRef<'a> { - fn raw_proc_macro(&self, id: DefIndex) -> &ProcMacro { + fn raw_proc_macro(self, id: DefIndex) -> &'a ProcMacro { // DefIndex's in root.proc_macro_data have a one-to-one correspondence // with items in 'raw_proc_macros'. let pos = self @@ -722,7 +714,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { &self.raw_proc_macros.unwrap()[pos] } - fn opt_item_ident(&self, item_index: DefIndex, sess: &Session) -> Option { + fn opt_item_ident(self, item_index: DefIndex, sess: &Session) -> Option { let name = self.def_key(item_index).disambiguated_data.data.get_opt_name()?; let span = match self.root.tables.ident_span.get(self, item_index) { Some(lazy_span) => lazy_span.decode((self, sess)), @@ -738,15 +730,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { Some(Ident::new(name, span)) } - fn item_ident(&self, item_index: DefIndex, sess: &Session) -> Ident { + fn item_ident(self, item_index: DefIndex, sess: &Session) -> Ident { self.opt_item_ident(item_index, sess).expect("no encoded ident for item") } - fn maybe_kind(&self, item_id: DefIndex) -> Option { + 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 { + 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 {}", @@ -757,7 +749,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - fn def_kind(&self, item_id: DefIndex) -> DefKind { + 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 {}", @@ -768,7 +760,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - fn get_span(&self, index: DefIndex, sess: &Session) -> Span { + fn get_span(self, index: DefIndex, sess: &Session) -> Span { self.root .tables .span @@ -777,7 +769,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, sess)) } - fn load_proc_macro(&self, id: DefIndex, sess: &Session) -> SyntaxExtension { + fn load_proc_macro(self, id: DefIndex, sess: &Session) -> SyntaxExtension { let (name, kind, helper_attrs) = match *self.raw_proc_macro(id) { ProcMacro::CustomDerive { trait_name, attributes, client } => { let helper_attrs = @@ -808,7 +800,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef { + fn get_trait_def(self, item_id: DefIndex, sess: &Session) -> ty::TraitDef { match self.kind(item_id) { EntryKind::Trait(data) => { let data = data.decode((self, sess)); @@ -821,6 +813,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { data.skip_array_during_method_dispatch, data.specialization_kind, self.def_path_hash(item_id), + data.must_implement_one_of, ) } EntryKind::TraitAlias => ty::TraitDef::new( @@ -832,13 +825,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { false, ty::trait_def::TraitSpecializationKind::None, self.def_path_hash(item_id), + None, ), _ => bug!("def-index does not refer to trait or trait alias"), } } fn get_variant( - &self, + self, kind: &EntryKind, index: DefIndex, parent_did: DefId, @@ -863,7 +857,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let ctor_did = data.ctor.map(|index| self.local_def_id(index)); ty::VariantDef::new( - self.item_ident(index, sess), + self.item_ident(index, sess).name, variant_did, ctor_did, data.discr, @@ -875,7 +869,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode(self) .map(|index| ty::FieldDef { did: self.local_def_id(index), - ident: self.item_ident(index, sess), + name: self.item_ident(index, sess).name, vis: self.get_visibility(index), }) .collect(), @@ -887,7 +881,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - fn get_adt_def(&self, item_id: DefIndex, tcx: TyCtxt<'tcx>) -> &'tcx ty::AdtDef { + fn get_adt_def(self, item_id: DefIndex, tcx: TyCtxt<'tcx>) -> &'tcx ty::AdtDef { let kind = self.kind(item_id); let did = self.local_def_id(item_id); @@ -915,7 +909,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_explicit_predicates( - &self, + self, item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> ty::GenericPredicates<'tcx> { @@ -923,7 +917,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_inferred_outlives( - &self, + self, item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { @@ -936,7 +930,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_super_predicates( - &self, + self, item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> ty::GenericPredicates<'tcx> { @@ -944,7 +938,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_explicit_item_bounds( - &self, + self, item_id: DefIndex, tcx: TyCtxt<'tcx>, ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { @@ -956,11 +950,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .unwrap_or_default() } - fn get_generics(&self, item_id: DefIndex, sess: &Session) -> ty::Generics { + fn get_generics(self, item_id: DefIndex, sess: &Session) -> ty::Generics { self.root.tables.generics.get(self, item_id).unwrap().decode((self, sess)) } - fn get_type(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + fn get_type(self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { self.root .tables .ty @@ -969,59 +963,63 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } - fn get_stability(&self, id: DefIndex) -> Option { + fn get_stability(self, id: DefIndex) -> Option { self.root.tables.stability.get(self, id).map(|stab| stab.decode(self)) } - fn get_const_stability(&self, id: DefIndex) -> Option { + fn get_const_stability(self, id: DefIndex) -> Option { self.root.tables.const_stability.get(self, id).map(|stab| stab.decode(self)) } - fn get_deprecation(&self, id: DefIndex) -> Option { + fn get_deprecation(self, id: DefIndex) -> Option { self.root.tables.deprecation.get(self, id).map(|depr| depr.decode(self)) } - fn get_visibility(&self, id: DefIndex) -> ty::Visibility { + fn get_visibility(self, id: DefIndex) -> ty::Visibility { self.root.tables.visibility.get(self, id).unwrap().decode(self) } - fn get_impl_data(&self, id: DefIndex) -> ImplData { + fn get_impl_data(self, id: DefIndex) -> ImplData { match self.kind(id) { EntryKind::Impl(data) => data.decode(self), _ => bug!(), } } - fn get_parent_impl(&self, id: DefIndex) -> Option { + fn get_parent_impl(self, id: DefIndex) -> Option { self.get_impl_data(id).parent_impl } - fn get_impl_polarity(&self, id: DefIndex) -> ty::ImplPolarity { + fn get_impl_polarity(self, id: DefIndex) -> ty::ImplPolarity { self.get_impl_data(id).polarity } - fn get_impl_defaultness(&self, id: DefIndex) -> hir::Defaultness { + fn get_impl_defaultness(self, id: DefIndex) -> hir::Defaultness { self.get_impl_data(id).defaultness } - fn get_impl_constness(&self, id: DefIndex) -> hir::Constness { + fn get_impl_constness(self, id: DefIndex) -> hir::Constness { self.get_impl_data(id).constness } - fn get_coerce_unsized_info(&self, id: DefIndex) -> Option { + fn get_trait_item_def_id(self, id: DefIndex) -> Option { + self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode(self)) + } + + fn get_coerce_unsized_info(self, id: DefIndex) -> Option { self.get_impl_data(id).coerce_unsized_info } - fn get_impl_trait(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Option> { + fn get_impl_trait(self, id: DefIndex, tcx: TyCtxt<'tcx>) -> Option> { self.root.tables.impl_trait_ref.get(self, id).map(|tr| tr.decode((self, tcx))) } - fn get_expn_that_defined(&self, id: DefIndex, sess: &Session) -> ExpnId { + fn get_expn_that_defined(self, id: DefIndex, sess: &Session) -> ExpnId { self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess)) } fn get_const_param_default( - &self, + self, tcx: TyCtxt<'tcx>, id: DefIndex, ) -> rustc_middle::ty::Const<'tcx> { @@ -1029,75 +1027,66 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } /// Iterates over all the stability attributes in the given crate. - fn get_lib_features(&self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option)] { - // FIXME: For a proc macro crate, not sure whether we should return the "host" - // features or an empty Vec. Both don't cause ICEs. + fn get_lib_features(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option)] { tcx.arena.alloc_from_iter(self.root.lib_features.decode(self)) } /// Iterates over the language items in the given crate. - fn get_lang_items(&self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] { - if self.root.is_proc_macro_crate() { - // Proc macro crates do not export any lang-items to the target. - &[] - } else { - tcx.arena.alloc_from_iter( - self.root - .lang_items - .decode(self) - .map(|(def_index, index)| (self.local_def_id(def_index), index)), - ) - } + fn get_lang_items(self) -> impl Iterator + 'a { + self.root + .lang_items + .decode(self) + .map(move |(def_index, index)| (self.local_def_id(def_index), index)) } /// Iterates over the diagnostic items in the given crate. - fn get_diagnostic_items(&self) -> DiagnosticItems { - if self.root.is_proc_macro_crate() { - // Proc macro crates do not export any diagnostic-items to the target. - Default::default() - } else { - let mut id_to_name = FxHashMap::default(); - let name_to_id = self - .root - .diagnostic_items - .decode(self) - .map(|(name, def_index)| { - let id = self.local_def_id(def_index); - id_to_name.insert(id, name); - (name, id) - }) - .collect(); - DiagnosticItems { id_to_name, name_to_id } - } + fn get_diagnostic_items(self) -> DiagnosticItems { + let mut id_to_name = FxHashMap::default(); + let name_to_id = self + .root + .diagnostic_items + .decode(self) + .map(|(name, def_index)| { + let id = self.local_def_id(def_index); + id_to_name.insert(id, name); + (name, id) + }) + .collect(); + DiagnosticItems { id_to_name, name_to_id } } - /// Iterates over each child of the given item. - fn each_child_of_item(&self, id: DefIndex, mut callback: impl FnMut(Export), sess: &Session) { + /// Iterates over all named children of the given module, + /// including both proper items and reexports. + /// Module here is understood in name resolution sense - it can be a `mod` item, + /// or a crate root, or an enum, or a trait. + fn for_each_module_child( + self, + id: DefIndex, + mut callback: impl FnMut(ModChild), + sess: &Session, + ) { if let Some(data) = &self.root.proc_macro_data { - /* If we are loading as a proc macro, we want to return the view of this crate - * as a proc macro crate. - */ + // If we are loading as a proc macro, we want to return + // the view of this crate as a proc macro crate. if id == CRATE_DEF_INDEX { - let macros = data.macros.decode(self); - for def_index in macros { + for def_index in data.macros.decode(self) { let raw_macro = self.raw_proc_macro(def_index); let res = Res::Def( DefKind::Macro(macro_kind(raw_macro)), self.local_def_id(def_index), ); let ident = self.item_ident(def_index, sess); - callback(Export { ident, res, vis: ty::Visibility::Public, span: ident.span }); + callback(ModChild { + ident, + res, + vis: ty::Visibility::Public, + span: ident.span, + }); } } return; } - // Find the item. - let kind = match self.maybe_kind(id) { - None => return, - Some(kind) => kind, - }; - // Iterate over all children. if let Some(children) = self.root.tables.children.get(self, id) { for child_index in children.decode((self, sess)) { @@ -1113,7 +1102,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let vis = self.get_visibility(child_index); let span = self.get_span(child_index, sess); - callback(Export { ident, res, vis, span }); + callback(ModChild { ident, res, vis, span }); // For non-re-export structs and variants add their constructors to children. // Re-export lists automatically contain constructors when necessary. @@ -1125,7 +1114,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); let vis = self.get_visibility(ctor_def_id.index); - callback(Export { res: ctor_res, vis, ident, span }); + callback(ModChild { ident, res: ctor_res, vis, span }); } } DefKind::Variant => { @@ -1150,7 +1139,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { vis = ty::Visibility::Restricted(crate_def_id); } } - callback(Export { res: ctor_res, ident, vis, span }); + callback(ModChild { ident, res: ctor_res, vis, span }); } _ => {} } @@ -1158,22 +1147,26 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - if let EntryKind::Mod(exports) = kind { - for exp in exports.decode((self, sess)) { - callback(exp); + match self.kind(id) { + EntryKind::Mod(exports) => { + for exp in exports.decode((self, sess)) { + callback(exp); + } } + EntryKind::Enum(..) | EntryKind::Trait(..) => {} + _ => bug!("`for_each_module_child` is called on a non-module: {:?}", self.def_kind(id)), } } - fn is_ctfe_mir_available(&self, id: DefIndex) -> bool { + 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 { + fn is_item_mir_available(self, id: DefIndex) -> bool { self.root.tables.mir.get(self, id).is_some() } - fn module_expansion(&self, id: DefIndex, sess: &Session) -> ExpnId { + fn module_expansion(self, id: DefIndex, sess: &Session) -> ExpnId { match self.kind(id) { EntryKind::Mod(_) | EntryKind::Enum(_) | EntryKind::Trait(_) => { self.get_expn_that_defined(id, sess) @@ -1182,7 +1175,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { + fn get_optimized_mir(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { self.root .tables .mir @@ -1193,7 +1186,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } - fn get_mir_for_ctfe(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { + fn get_mir_for_ctfe(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { self.root .tables .mir_for_ctfe @@ -1205,7 +1198,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_thir_abstract_const( - &self, + self, tcx: TyCtxt<'tcx>, id: DefIndex, ) -> Result]>, ErrorReported> { @@ -1216,7 +1209,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .map_or(Ok(None), |v| Ok(Some(v.decode((self, tcx))))) } - fn get_unused_generic_params(&self, id: DefIndex) -> FiniteBitSet { + fn get_unused_generic_params(self, id: DefIndex) -> FiniteBitSet { self.root .tables .unused_generic_params @@ -1225,7 +1218,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .unwrap_or_default() } - fn get_promoted_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> IndexVec> { + fn get_promoted_mir(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> IndexVec> { self.root .tables .promoted_mir @@ -1236,7 +1229,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } - fn mir_const_qualif(&self, id: DefIndex) -> mir::ConstQualifs { + fn mir_const_qualif(self, id: DefIndex) -> mir::ConstQualifs { match self.kind(id) { EntryKind::AnonConst(qualif, _) | EntryKind::Const(qualif, _) @@ -1251,14 +1244,24 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_fn_has_self_parameter(&self, id: DefIndex) -> bool { + fn get_fn_has_self_parameter(self, id: DefIndex) -> bool { match self.kind(id) { EntryKind::AssocFn(data) => data.decode(self).has_self, _ => false, } } - fn get_associated_item(&self, id: DefIndex, sess: &Session) -> ty::AssocItem { + fn get_associated_item_def_ids(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [DefId] { + if let Some(children) = self.root.tables.children.get(self, id) { + tcx.arena.alloc_from_iter( + children.decode((self, tcx.sess)).map(|child_index| self.local_def_id(child_index)), + ) + } else { + &[] + } + } + + fn get_associated_item(self, id: DefIndex, sess: &Session) -> ty::AssocItem { let def_key = self.def_key(id); let parent = self.local_def_id(def_key.parent.unwrap()); let ident = self.item_ident(id, sess); @@ -1274,21 +1277,22 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }; ty::AssocItem { - ident, + name: ident.name, kind, vis: self.get_visibility(id), defaultness: container.defaultness(), def_id: self.local_def_id(id), + trait_item_def_id: self.get_trait_item_def_id(id), container: container.with_def_id(parent), fn_has_self_parameter: has_self, } } - fn get_item_variances(&'a self, id: DefIndex) -> impl Iterator + 'a { + fn get_item_variances(self, id: DefIndex) -> impl Iterator + 'a { self.root.tables.variances.get(self, id).unwrap_or_else(Lazy::empty).decode(self) } - fn get_ctor_def_id_and_kind(&self, node_id: DefIndex) -> Option<(DefId, CtorKind)> { + fn get_ctor_def_id_and_kind(self, node_id: DefIndex) -> Option<(DefId, CtorKind)> { match self.kind(node_id) { EntryKind::Struct(data, _) | EntryKind::Variant(data) => { let vdata = data.decode(self); @@ -1299,7 +1303,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_item_attrs( - &'a self, + self, id: DefIndex, sess: &'a Session, ) -> impl Iterator + 'a { @@ -1323,30 +1327,32 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, sess)) } - fn get_struct_field_names(&self, id: DefIndex, sess: &Session) -> Vec> { + fn get_struct_field_names( + self, + id: DefIndex, + sess: &'a Session, + ) -> impl Iterator> + 'a { self.root .tables .children .get(self, id) .unwrap_or_else(Lazy::empty) .decode(self) - .map(|index| respan(self.get_span(index, sess), self.item_ident(index, sess).name)) - .collect() + .map(move |index| respan(self.get_span(index, sess), self.item_ident(index, sess).name)) } - fn get_struct_field_visibilities(&self, id: DefIndex) -> Vec { + fn get_struct_field_visibilities(self, id: DefIndex) -> impl Iterator + 'a { self.root .tables .children .get(self, id) .unwrap_or_else(Lazy::empty) .decode(self) - .map(|field_index| self.get_visibility(field_index)) - .collect() + .map(move |field_index| self.get_visibility(field_index)) } fn get_inherent_implementations_for_type( - &self, + self, tcx: TyCtxt<'tcx>, id: DefIndex, ) -> &'tcx [DefId] { @@ -1361,25 +1367,45 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - fn get_traits(&'a self) -> impl Iterator + 'a { - self.root.traits.decode(self).map(|index| self.local_def_id(index)) + /// Decodes all inherent impls in the crate (for rustdoc). + fn get_inherent_impls(self) -> impl Iterator + 'a { + (0..self.root.tables.inherent_impls.size()).flat_map(move |i| { + let ty_index = DefIndex::from_usize(i); + let ty_def_id = self.local_def_id(ty_index); + self.root + .tables + .inherent_impls + .get(self, ty_index) + .unwrap_or_else(Lazy::empty) + .decode(self) + .map(move |impl_index| (ty_def_id, self.local_def_id(impl_index))) + }) } - fn get_trait_impls(&'a self) -> impl Iterator)> + 'a { - self.trait_impls.values().flat_map(move |impls| { - impls - .decode(self) - .map(|(idx, simplified_self_ty)| (self.local_def_id(idx), simplified_self_ty)) + /// Decodes all traits in the crate (for rustdoc and rustc diagnostics). + fn get_traits(self) -> impl Iterator + 'a { + self.root.traits.decode(self).map(move |index| self.local_def_id(index)) + } + + /// Decodes all trait impls in the crate (for rustdoc). + fn get_trait_impls(self) -> impl Iterator)> + 'a { + self.cdata.trait_impls.iter().flat_map(move |((trait_cnum_raw, trait_index), impls)| { + let trait_def_id = DefId { + krate: self.cnum_map[CrateNum::from_u32(*trait_cnum_raw)], + index: *trait_index, + }; + impls.decode(self).map(move |(impl_index, simplified_self_ty)| { + (trait_def_id, self.local_def_id(impl_index), simplified_self_ty) + }) }) } fn get_implementations_of_trait( - &self, + self, tcx: TyCtxt<'tcx>, trait_def_id: DefId, ) -> &'tcx [(DefId, Option)] { - if self.root.is_proc_macro_crate() { - // proc-macro crates export no trait impls. + if self.trait_impls.is_empty() { return &[]; } @@ -1401,7 +1427,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_trait_of_item(&self, id: DefIndex) -> Option { + fn get_trait_of_item(self, id: DefIndex) -> Option { let def_key = self.def_key(id); match def_key.disambiguated_data.data { DefPathData::TypeNs(..) | DefPathData::ValueNs(..) => (), @@ -1414,16 +1440,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - fn get_native_libraries(&self, sess: &Session) -> Vec { - if self.root.is_proc_macro_crate() { - // Proc macro crates do not have any *target* native libraries. - vec![] - } else { - self.root.native_libraries.decode((self, sess)).collect() - } + fn get_native_libraries(self, sess: &'a Session) -> impl Iterator + 'a { + self.root.native_libraries.decode((self, sess)) } - fn get_proc_macro_quoted_span(&self, index: usize, sess: &Session) -> Span { + fn get_proc_macro_quoted_span(self, index: usize, sess: &Session) -> Span { self.root .tables .proc_macro_quoted_spans @@ -1432,19 +1453,12 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, sess)) } - fn get_foreign_modules(&self, tcx: TyCtxt<'tcx>) -> Lrc> { - if self.root.is_proc_macro_crate() { - // Proc macro crates do not have any *target* foreign modules. - Lrc::new(FxHashMap::default()) - } else { - let modules: FxHashMap = - self.root.foreign_modules.decode((self, tcx.sess)).map(|m| (m.def_id, m)).collect(); - Lrc::new(modules) - } + fn get_foreign_modules(self, sess: &'a Session) -> impl Iterator + '_ { + self.root.foreign_modules.decode((self, sess)) } fn get_dylib_dependency_formats( - &self, + self, tcx: TyCtxt<'tcx>, ) -> &'tcx [(CrateNum, LinkagePreference)] { tcx.arena.alloc_from_iter( @@ -1455,16 +1469,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - fn get_missing_lang_items(&self, tcx: TyCtxt<'tcx>) -> &'tcx [lang_items::LangItem] { - if self.root.is_proc_macro_crate() { - // Proc macro crates do not depend on any target weak lang-items. - &[] - } else { - tcx.arena.alloc_from_iter(self.root.lang_items_missing.decode(self)) - } + fn get_missing_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [lang_items::LangItem] { + tcx.arena.alloc_from_iter(self.root.lang_items_missing.decode(self)) } - fn get_fn_param_names(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [Ident] { + fn get_fn_param_names(self, tcx: TyCtxt<'tcx>, id: DefIndex) -> &'tcx [Ident] { let param_names = match self.kind(id) { EntryKind::Fn(data) | EntryKind::ForeignFn(data) => data.decode(self).param_names, EntryKind::AssocFn(data) => data.decode(self).fn_data.param_names, @@ -1474,19 +1483,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn exported_symbols( - &self, + self, tcx: TyCtxt<'tcx>, ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { - if self.root.is_proc_macro_crate() { - // If this crate is a custom derive crate, then we're not even going to - // link those in so we skip those crates. - &[] - } else { - tcx.arena.alloc_from_iter(self.root.exported_symbols.decode((self, tcx))) - } + tcx.arena.alloc_from_iter(self.root.exported_symbols.decode((self, tcx))) } - fn get_rendered_const(&self, id: DefIndex) -> String { + fn get_rendered_const(self, id: DefIndex) -> String { match self.kind(id) { EntryKind::AnonConst(_, data) | EntryKind::Const(_, data) @@ -1495,7 +1498,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_macro(&self, id: DefIndex, sess: &Session) -> MacroDef { + fn get_macro(self, id: DefIndex, sess: &Session) -> MacroDef { match self.kind(id) { EntryKind::MacroDef(macro_def) => macro_def.decode((self, sess)), _ => bug!(), @@ -1504,7 +1507,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // This replicates some of the logic of the crate-local `is_const_fn_raw` query, because we // don't serialize constness for tuple variant and tuple struct constructors. - fn is_const_fn_raw(&self, id: DefIndex) -> bool { + fn is_const_fn_raw(self, id: DefIndex) -> bool { let constness = match self.kind(id) { EntryKind::AssocFn(data) => data.decode(self).fn_data.constness, EntryKind::Fn(data) => data.decode(self).constness, @@ -1515,7 +1518,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { constness == hir::Constness::Const } - fn asyncness(&self, id: DefIndex) -> hir::IsAsync { + fn asyncness(self, id: DefIndex) -> hir::IsAsync { match self.kind(id) { EntryKind::Fn(data) => data.decode(self).asyncness, EntryKind::AssocFn(data) => data.decode(self).fn_data.asyncness, @@ -1524,7 +1527,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn is_foreign_item(&self, id: DefIndex) -> bool { + fn is_foreign_item(self, id: DefIndex) -> bool { match self.kind(id) { EntryKind::ForeignImmStatic | EntryKind::ForeignMutStatic | EntryKind::ForeignFn(_) => { true @@ -1533,7 +1536,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn static_mutability(&self, id: DefIndex) -> Option { + fn static_mutability(self, id: DefIndex) -> Option { match self.kind(id) { EntryKind::ImmStatic | EntryKind::ForeignImmStatic => Some(hir::Mutability::Not), EntryKind::MutStatic | EntryKind::ForeignMutStatic => Some(hir::Mutability::Mut), @@ -1541,19 +1544,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn generator_kind(&self, id: DefIndex) -> Option { + fn generator_kind(self, id: DefIndex) -> Option { match self.kind(id) { EntryKind::Generator(data) => Some(data), _ => None, } } - fn fn_sig(&self, id: DefIndex, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { + fn fn_sig(self, id: DefIndex, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { self.root.tables.fn_sig.get(self, id).unwrap().decode((self, tcx)) } #[inline] - fn def_key(&self, index: DefIndex) -> DefKey { + fn def_key(self, index: DefIndex) -> DefKey { *self .def_key_cache .lock() @@ -1562,13 +1565,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } // Returns the path leading to the thing with this `id`. - fn def_path(&self, id: DefIndex) -> DefPath { + fn def_path(self, id: DefIndex) -> DefPath { debug!("def_path(cnum={:?}, id={:?})", self.cnum, id); DefPath::make(self.cnum, id, |parent| self.def_key(parent)) } fn def_path_hash_unlocked( - &self, + self, index: DefIndex, def_path_hashes: &mut FxHashMap, ) -> DefPathHash { @@ -1578,17 +1581,17 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } #[inline] - fn def_path_hash(&self, index: DefIndex) -> DefPathHash { + fn def_path_hash(self, index: DefIndex) -> DefPathHash { let mut def_path_hashes = self.def_path_hash_cache.lock(); self.def_path_hash_unlocked(index, &mut def_path_hashes) } #[inline] - fn def_path_hash_to_def_index(&self, hash: DefPathHash) -> DefIndex { + fn def_path_hash_to_def_index(self, hash: DefPathHash) -> DefIndex { self.def_path_hash_map.def_path_hash_to_def_index(&hash) } - fn expn_hash_to_expn_id(&self, sess: &Session, index_guess: u32, hash: ExpnHash) -> ExpnId { + fn expn_hash_to_expn_id(self, sess: &Session, index_guess: u32, hash: ExpnHash) -> ExpnId { debug_assert_eq!(ExpnId::from_hash(hash), None); let index_guess = ExpnIndex::from_u32(index_guess); let old_hash = self.root.expn_hashes.get(self, index_guess).map(|lazy| lazy.decode(self)); @@ -1646,7 +1649,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { /// /// Proc macro crates don't currently export spans, so this function does not have /// to work for them. - fn imported_source_files(&self, sess: &Session) -> &'a [ImportedSourceFile] { + fn imported_source_files(self, sess: &Session) -> &'a [ImportedSourceFile] { // Translate the virtual `/rustc/$hash` prefix back to a real directory // that should hold actual sources, where possible. // @@ -1870,7 +1873,7 @@ impl CrateMetadata { cnum_map, dependencies, dep_kind: Lock::new(dep_kind), - source, + source: Lrc::new(source), private_dep, host_hash, extern_crate: Lock::new(None), @@ -1898,7 +1901,7 @@ impl CrateMetadata { } crate fn source(&self) -> &CrateSource { - &self.source + &*self.source } crate fn dep_kind(&self) -> CrateDepKind { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index aac0aa61ea..7708b5193f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -3,16 +3,16 @@ use crate::foreign_modules; use crate::native_libs; use rustc_ast as ast; -use rustc_data_structures::stable_map::FxHashMap; -use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::{self, TyCtxt, Visibility}; -use rustc_session::cstore::{CrateSource, CrateStore, ForeignModule}; +use rustc_session::cstore::{CrateSource, CrateStore}; use rustc_session::utils::NativeLibKind; use rustc_session::{Session, StableCrateId}; use rustc_span::hygiene::{ExpnHash, ExpnId}; @@ -103,12 +103,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, tcx.calculate_dtor(def_id, |_,_| Ok(())) } variances_of => { tcx.arena.alloc_from_iter(cdata.get_item_variances(def_id.index)) } - associated_item_def_ids => { - let mut result = SmallVec::<[_; 8]>::new(); - cdata.each_child_of_item(def_id.index, - |child| result.push(child.res.def_id()), tcx.sess); - tcx.arena.alloc_slice(&result) - } + associated_item_def_ids => { cdata.get_associated_item_def_ids(tcx, def_id.index) } associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) } impl_trait_ref => { cdata.get_impl_trait(def_id.index, tcx) } impl_polarity => { cdata.get_impl_polarity(def_id.index) } @@ -122,7 +117,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) } thir_abstract_const => { cdata.get_thir_abstract_const(tcx, def_id.index) } unused_generic_params => { cdata.get_unused_generic_params(def_id.index) } - const_param_default => { tcx.mk_const(cdata.get_const_param_default(tcx, def_id.index)) } + const_param_default => { cdata.get_const_param_default(tcx, def_id.index) } mir_const_qualif => { cdata.mir_const_qualif(def_id.index) } fn_sig => { cdata.fn_sig(def_id.index, tcx) } inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } @@ -183,8 +178,8 @@ provide! { <'tcx> tcx, def_id, other, cdata, reachable_non_generics } - native_libraries => { Lrc::new(cdata.get_native_libraries(tcx.sess)) } - foreign_modules => { cdata.get_foreign_modules(tcx) } + native_libraries => { cdata.get_native_libraries(tcx.sess).collect() } + foreign_modules => { cdata.get_foreign_modules(tcx.sess).map(|m| (m.def_id, m)).collect() } crate_hash => { cdata.root.hash } crate_host_hash => { cdata.host_hash } crate_name => { cdata.root.name } @@ -192,8 +187,6 @@ provide! { <'tcx> tcx, def_id, other, cdata, extra_filename => { cdata.root.extra_filename.clone() } traits_in_crate => { tcx.arena.alloc_from_iter(cdata.get_traits()) } - all_trait_implementations => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) } - implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) } visibility => { cdata.get_visibility(def_id.index) } @@ -201,13 +194,13 @@ provide! { <'tcx> tcx, def_id, other, cdata, let r = *cdata.dep_kind.lock(); r } - item_children => { + module_children => { let mut result = SmallVec::<[_; 8]>::new(); - cdata.each_child_of_item(def_id.index, |child| result.push(child), tcx.sess); + cdata.for_each_module_child(def_id.index, |child| result.push(child), tcx.sess); tcx.arena.alloc_slice(&result) } defined_lib_features => { cdata.get_lib_features(tcx) } - defined_lang_items => { cdata.get_lang_items(tcx) } + defined_lang_items => { tcx.arena.alloc_from_iter(cdata.get_lang_items()) } diagnostic_items => { cdata.get_diagnostic_items() } missing_lang_items => { cdata.get_missing_lang_items(tcx) } @@ -216,7 +209,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, r } - used_crate_source => { Lrc::new(cdata.source.clone()) } + used_crate_source => { Lrc::clone(&cdata.source) } exported_symbols => { let syms = cdata.exported_symbols(tcx); @@ -270,13 +263,11 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { }, native_libraries: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - Lrc::new(native_libs::collect(tcx)) + native_libs::collect(tcx) }, foreign_modules: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - let modules: FxHashMap = - foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect(); - Lrc::new(modules) + foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect() }, // Returns a map from a sufficiently visible external item (i.e., an @@ -314,35 +305,40 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { bfs_queue.push_back(DefId { krate: cnum, index: CRATE_DEF_INDEX }); } - let mut add_child = |bfs_queue: &mut VecDeque<_>, export: &Export, parent: DefId| { - if !export.vis.is_public() { + let mut add_child = |bfs_queue: &mut VecDeque<_>, child: &ModChild, parent: DefId| { + if !child.vis.is_public() { return; } - if let Some(child) = export.res.opt_def_id() { - if export.ident.name == kw::Underscore { - fallback_map.insert(child, parent); + if let Some(def_id) = child.res.opt_def_id() { + if child.ident.name == kw::Underscore { + fallback_map.insert(def_id, parent); return; } - match visible_parent_map.entry(child) { + match visible_parent_map.entry(def_id) { Entry::Occupied(mut entry) => { // If `child` is defined in crate `cnum`, ensure // that it is mapped to a parent in `cnum`. - if child.is_local() && entry.get().is_local() { + if def_id.is_local() && entry.get().is_local() { entry.insert(parent); } } Entry::Vacant(entry) => { entry.insert(parent); - bfs_queue.push_back(child); + if matches!( + child.res, + Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, _) + ) { + bfs_queue.push_back(def_id); + } } } } }; while let Some(def) = bfs_queue.pop_front() { - for child in tcx.item_children(def).iter() { + for child in tcx.module_children(def).iter() { add_child(bfs_queue, child, def); } } @@ -372,11 +368,18 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { } impl CStore { - pub fn struct_field_names_untracked(&self, def: DefId, sess: &Session) -> Vec> { + pub fn struct_field_names_untracked<'a>( + &'a self, + def: DefId, + sess: &'a Session, + ) -> impl Iterator> + 'a { self.get_crate_data(def.krate).get_struct_field_names(def.index, sess) } - pub fn struct_field_visibilities_untracked(&self, def: DefId) -> Vec { + pub fn struct_field_visibilities_untracked( + &self, + def: DefId, + ) -> impl Iterator + '_ { self.get_crate_data(def.krate).get_struct_field_visibilities(def.index) } @@ -388,9 +391,9 @@ impl CStore { self.get_crate_data(def.krate).get_visibility(def.index) } - pub fn item_children_untracked(&self, def_id: DefId, sess: &Session) -> Vec { + pub fn module_children_untracked(&self, def_id: DefId, sess: &Session) -> Vec { let mut result = vec![]; - self.get_crate_data(def_id.krate).each_child_of_item( + self.get_crate_data(def_id.krate).for_each_module_child( def_id.index, |child| result.push(child), sess, @@ -430,7 +433,7 @@ impl CStore { self.get_crate_data(def.krate).get_fn_has_self_parameter(def.index) } - pub fn crate_source_untracked(&self, cnum: CrateNum) -> CrateSource { + pub fn crate_source_untracked(&self, cnum: CrateNum) -> Lrc { self.get_crate_data(cnum).source.clone() } @@ -461,8 +464,12 @@ impl CStore { self.get_crate_data(cnum).num_def_ids() } - pub fn item_attrs_untracked(&self, def_id: DefId, sess: &Session) -> Vec { - self.get_crate_data(def_id.krate).get_item_attrs(def_id.index, sess).collect() + pub fn item_attrs_untracked<'a>( + &'a self, + def_id: DefId, + sess: &'a Session, + ) -> impl Iterator + 'a { + self.get_crate_data(def_id.krate).get_item_attrs(def_id.index, sess) } pub fn get_proc_macro_quoted_span_untracked( @@ -473,6 +480,32 @@ impl CStore { ) -> Span { self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess) } + + /// Decodes all traits in the crate (for rustdoc). + pub fn traits_in_crate_untracked(&self, cnum: CrateNum) -> impl Iterator + '_ { + self.get_crate_data(cnum).get_traits() + } + + /// Decodes all trait impls in the crate (for rustdoc). + pub fn trait_impls_in_crate_untracked( + &self, + cnum: CrateNum, + ) -> impl Iterator)> + '_ { + self.get_crate_data(cnum).get_trait_impls() + } + + /// Decodes all inherent impls in the crate (for rustdoc). + pub fn inherent_impls_in_crate_untracked( + &self, + cnum: CrateNum, + ) -> impl Iterator + '_ { + self.get_crate_data(cnum).get_inherent_impls() + } + + /// Decodes all lang items in the crate (for rustdoc). + pub fn lang_items_untracked(&self, cnum: CrateNum) -> impl Iterator + '_ { + self.get_crate_data(cnum).get_lang_items().map(|(def_id, _)| def_id) + } } impl CrateStore for CStore { diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 054431169a..d66f2b031a 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -39,11 +39,11 @@ impl<'a, 'tcx> Encodable> for DefPathHashMapRef<'tcx> { } impl<'a, 'tcx> Decodable> for DefPathHashMapRef<'static> { - fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result, String> { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> DefPathHashMapRef<'static> { // Import TyDecoder so we can access the DecodeContext::position() method use crate::rustc_middle::ty::codec::TyDecoder; - let len = d.read_usize()?; + let len = d.read_usize(); let pos = d.position(); let o = OwningRef::new(d.blob().clone()).map(|x| &x[pos..pos + len]); @@ -52,7 +52,9 @@ impl<'a, 'tcx> Decodable> for DefPathHashMapRef<'static> // the method. We use read_raw_bytes() for that. let _ = d.read_raw_bytes(len); - let inner = odht::HashTable::from_raw_bytes(o).map_err(|e| format!("{}", e))?; - Ok(DefPathHashMapRef::OwnedFromMetadata(inner)) + let inner = odht::HashTable::from_raw_bytes(o).unwrap_or_else(|e| { + panic!("decode error: {}", e); + }); + DefPathHashMapRef::OwnedFromMetadata(inner) } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 12d66f4fc4..4dea04e62f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -11,13 +11,13 @@ use rustc_hir::def_id::{ CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE, }; use rustc_hir::definitions::DefPathData; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::lang_items; use rustc_hir::{AnonConst, GenericParamKind}; use rustc_index::bit_set::GrowableBitSet; use rustc_index::vec::Idx; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::{ metadata_symbol_name, ExportedSymbol, SymbolExportLevel, @@ -26,7 +26,7 @@ use rustc_middle::mir::interpret; use rustc_middle::thir; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; -use rustc_middle::ty::fast_reject::{self, SimplifiedType, SimplifyParams, StripReferences}; +use rustc_middle::ty::fast_reject::{self, SimplifiedType, SimplifyParams}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt}; use rustc_serialize::{opaque, Encodable, Encoder}; @@ -404,24 +404,24 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { &mut self, lazy: Lazy, ) -> Result<(), ::Error> { - let min_end = lazy.position.get() + T::min_size(lazy.meta); + let pos = lazy.position.get(); let distance = match self.lazy_state { LazyState::NoNode => bug!("emit_lazy_distance: outside of a metadata node"), LazyState::NodeStart(start) => { let start = start.get(); - assert!(min_end <= start); - start - min_end + assert!(pos <= start); + start - pos } - LazyState::Previous(last_min_end) => { + LazyState::Previous(last_pos) => { assert!( - last_min_end <= lazy.position, + last_pos <= lazy.position, "make sure that the calls to `lazy*` \ are in the same order as the metadata fields", ); - lazy.position.get() - last_min_end.get() + lazy.position.get() - last_pos.get() } }; - self.lazy_state = LazyState::Previous(NonZeroUsize::new(min_end).unwrap()); + self.lazy_state = LazyState::Previous(NonZeroUsize::new(pos).unwrap()); self.emit_usize(distance) } @@ -436,7 +436,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let meta = value.encode_contents_for_lazy(self); self.lazy_state = LazyState::NoNode; - assert!(pos.get() + ::min_size(meta) <= self.position()); + assert!(pos.get() <= self.position()); Lazy::from_position_and_meta(pos, meta) } @@ -982,7 +982,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { 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 }; + let Some(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, @@ -1052,7 +1052,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { assert!(f.did.is_local()); f.did.index })); - self.encode_ident_span(def_id, variant.ident); + self.encode_ident_span(def_id, variant.ident(tcx)); self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { // FIXME(eddyb) encode signature only in `encode_enum_variant_ctor`. @@ -1094,7 +1094,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // code uses it). However, we skip encoding anything relating to child // items - we encode information about proc-macros later on. let reexports = if !self.is_proc_macro { - match tcx.module_exports(local_def_id) { + match tcx.module_reexports(local_def_id) { Some(exports) => self.lazy(exports), _ => Lazy::empty(), } @@ -1104,7 +1104,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.kind[def_id] <- EntryKind::Mod(reexports)); if self.is_proc_macro { - record!(self.tables.children[def_id] <- &[]); // Encode this here because we don't do it in encode_def_ids. record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); } else { @@ -1139,7 +1138,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EncodeContext::encode_field({:?})", def_id); record!(self.tables.kind[def_id] <- EntryKind::Field); - self.encode_ident_span(def_id, field.ident); + self.encode_ident_span(def_id, field.ident(self.tcx)); self.encode_item_type(def_id); } @@ -1292,8 +1291,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.kind[def_id] <- EntryKind::AssocType(container)); } } - self.encode_ident_span(def_id, impl_item.ident); + self.encode_ident_span(def_id, impl_item.ident(self.tcx)); self.encode_item_type(def_id); + if let Some(trait_item_def_id) = impl_item.trait_item_def_id { + record!(self.tables.trait_item_def_id[def_id] <- trait_item_def_id); + } if impl_item.kind == ty::AssocKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } @@ -1312,7 +1314,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { return; } - let mut keys_and_jobs = self + let keys_and_jobs = self .tcx .mir_keys(()) .iter() @@ -1325,8 +1327,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } }) .collect::>(); - // Sort everything to ensure a stable order for diagnotics. - keys_and_jobs.sort_by_key(|&(def_id, _, _)| def_id.index()); for (def_id, encode_const, encode_opt) in keys_and_jobs.into_iter() { debug_assert!(encode_const || encode_opt); @@ -1511,6 +1511,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { is_marker: trait_def.is_marker, skip_array_during_method_dispatch: trait_def.skip_array_during_method_dispatch, specialization_kind: trait_def.specialization_kind, + must_implement_one_of: trait_def.must_implement_one_of.clone(), }; EntryKind::Trait(self.lazy(data)) @@ -1577,12 +1578,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - fn encode_info_for_closure(&mut self, def_id: LocalDefId) { + fn encode_info_for_closure(&mut self, hir_id: hir::HirId) { + let def_id = self.tcx.hir().local_def_id(hir_id); debug!("EncodeContext::encode_info_for_closure({:?})", def_id); // NOTE(eddyb) `tcx.type_of(def_id)` isn't used because it's fully generic, // including on the signature, which is inferred in `typeck. - 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); match ty.kind() { @@ -1603,9 +1604,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - fn encode_info_for_anon_const(&mut self, def_id: LocalDefId) { + fn encode_info_for_anon_const(&mut self, id: hir::HirId) { + let def_id = self.tcx.hir().local_def_id(id); debug!("EncodeContext::encode_info_for_anon_const({:?})", def_id); - let id = self.tcx.hir().local_def_id_to_hir_id(def_id); let body_id = self.tcx.hir().body_owned_by(id); let const_data = self.encode_rendered_const_for_body(body_id); let qualifs = self.tcx.mir_const_qualif(def_id); @@ -1741,7 +1742,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hash: self.tcx.crate_hash(cnum), host_hash: self.tcx.crate_host_hash(cnum), kind: self.tcx.dep_kind(cnum), - extra_filename: self.tcx.extra_filename(cnum), + extra_filename: self.tcx.extra_filename(cnum).clone(), }; (cnum, dep) }) @@ -1915,10 +1916,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // FIXME(eddyb) make metadata encoding walk over all definitions, instead of HIR. impl<'a, 'tcx> Visitor<'tcx> for EncodeContext<'a, 'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { intravisit::walk_expr(self, ex); @@ -1926,8 +1927,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EncodeContext<'a, 'tcx> { } fn visit_anon_const(&mut self, c: &'tcx AnonConst) { intravisit::walk_anon_const(self, c); - let def_id = self.tcx.hir().local_def_id(c.hir_id); - self.encode_info_for_anon_const(def_id); + self.encode_info_for_anon_const(c.hir_id); } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { intravisit::walk_item(self, item); @@ -1981,8 +1981,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_info_for_expr(&mut self, expr: &hir::Expr<'_>) { if let hir::ExprKind::Closure(..) = expr.kind { - let def_id = self.tcx.hir().local_def_id(expr.hir_id); - self.encode_info_for_closure(def_id); + self.encode_info_for_closure(expr.hir_id); } } @@ -2067,7 +2066,6 @@ impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplsVisitor<'tcx> { self.tcx, trait_ref.self_ty(), SimplifyParams::No, - StripReferences::No, ); self.impls diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4076e0b9e0..8424a31d59 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -12,7 +12,7 @@ use rustc_hir::def_id::{DefId, DefIndex, DefPathHash, StableCrateId}; use rustc_hir::definitions::DefKey; use rustc_hir::lang_items; use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use rustc_middle::mir; use rustc_middle::thir; @@ -63,27 +63,14 @@ pub const METADATA_HEADER: &[u8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_V /// e.g. for `Lazy<[T]>`, this is the length (count of `T` values). trait LazyMeta { type Meta: Copy + 'static; - - /// Returns the minimum encoded size. - // FIXME(eddyb) Give better estimates for certain types. - fn min_size(meta: Self::Meta) -> usize; } impl LazyMeta for T { type Meta = (); - - fn min_size(_: ()) -> usize { - assert_ne!(std::mem::size_of::(), 0); - 1 - } } impl LazyMeta for [T] { type Meta = usize; - - fn min_size(len: usize) -> usize { - len * T::min_size(()) - } } /// A value of type T referred to by its absolute position @@ -161,8 +148,7 @@ enum LazyState { NodeStart(NonZeroUsize), /// Inside a metadata node, with a previous `Lazy`. - /// The position is a conservative estimate of where that - /// previous `Lazy` would end (see their comments). + /// The position is where that previous `Lazy` would start. Previous(NonZeroUsize), } @@ -302,6 +288,7 @@ define_tables! { ty: Table)>, fn_sig: Table)>, impl_trait_ref: Table)>, + trait_item_def_id: Table>, inherent_impls: Table>, variances: Table>, generics: Table>, @@ -349,7 +336,7 @@ enum EntryKind { Union(Lazy, ReprOptions), Fn(Lazy), ForeignFn(Lazy), - Mod(Lazy<[Export]>), + Mod(Lazy<[ModChild]>), MacroDef(Lazy), ProcMacro(MacroKind), Closure, @@ -391,6 +378,7 @@ struct TraitData { is_marker: bool, skip_array_during_method_dispatch: bool, specialization_kind: ty::trait_def::TraitSpecializationKind, + must_implement_one_of: Option>, } #[derive(TyEncodable, TyDecodable)] diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 4dfefda490..265ca5a6d8 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -183,10 +183,6 @@ where Option: FixedSizeEncoding, { type Meta = usize; - - fn min_size(len: usize) -> usize { - len - } } impl Lazy> diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index a9db846938..b133441023 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -12,8 +12,8 @@ bitflags = "1.2.1" either = "1.5.0" gsgdt = "0.1.2" tracing = "0.1" -rustc-rayon = "0.3.1" -rustc-rayon-core = "0.3.1" +rustc-rayon = "0.3.2" +rustc-rayon-core = "0.3.2" polonius-engine = "0.13.0" rustc_apfloat = { path = "../rustc_apfloat" } rustc_attr = { path = "../rustc_attr" } @@ -29,7 +29,7 @@ rustc_index = { path = "../rustc_index" } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.75.0" +chalk-ir = "0.76.0" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_session = { path = "../rustc_session" } rustc_type_ir = { path = "../rustc_type_ir" } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index a936852f4e..c4e6734aa0 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -52,6 +52,9 @@ macro_rules! arena_types { Vec> > >, + [] dtorck_constraint: rustc_middle::traits::query::DtorckConstraint<'tcx>, + [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>, + [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>, [] type_op_subtype: rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx, ()> @@ -85,7 +88,8 @@ macro_rules! arena_types { // Interned types [] tys: rustc_middle::ty::TyS<'tcx>, - [] predicates: rustc_middle::ty::PredicateInner<'tcx>, + [] predicates: rustc_middle::ty::PredicateS<'tcx>, + [] consts: rustc_middle::ty::ConstS<'tcx>, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena @@ -95,6 +99,7 @@ macro_rules! arena_types { // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. [decode] span: rustc_span::Span, [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet, + [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, [] dep_kind: rustc_middle::dep_graph::DepKindStruct, ]); diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 5c7cdbe4c2..d20be0a34d 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -266,7 +266,9 @@ impl DepNodeExt for DepNode { /// has been removed. fn extract_def_id<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option { if self.kind.fingerprint_style(tcx) == FingerprintStyle::DefPathHash { - Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into()))) + Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into()), &mut || { + panic!("Failed to extract DefId: {:?} {}", self.kind, self.hash) + })) } else { None } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 79d7ca32f3..6bfd1b7ffa 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -1,6 +1,5 @@ use crate::ty::{self, TyCtxt}; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::sync::Lock; use rustc_query_system::ich::StableHashingContext; use rustc_session::Session; @@ -17,6 +16,7 @@ crate use dep_node::{make_compile_codegen_unit, make_compile_mono_item}; pub type DepGraph = rustc_query_system::dep_graph::DepGraph; pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps; +pub type TaskDepsRef<'a> = rustc_query_system::dep_graph::TaskDepsRef<'a, DepKind>; pub type DepGraphQuery = rustc_query_system::dep_graph::DepGraphQuery; pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph; pub type EdgeFilter = rustc_query_system::dep_graph::debug::EdgeFilter; @@ -45,7 +45,7 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { write!(f, ")") } - fn with_deps(task_deps: Option<&Lock>, op: OP) -> R + fn with_deps(task_deps: TaskDepsRef<'_>, op: OP) -> R where OP: FnOnce() -> R, { @@ -58,10 +58,10 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { fn read_deps(op: OP) where - OP: for<'a> FnOnce(Option<&'a Lock>), + OP: for<'a> FnOnce(TaskDepsRef<'a>), { ty::tls::with_context_opt(|icx| { - let icx = if let Some(icx) = icx { icx } else { return }; + let Some(icx) = icx else { return }; op(icx.task_deps) }) } diff --git a/compiler/rustc_middle/src/hir/exports.rs b/compiler/rustc_middle/src/hir/exports.rs deleted file mode 100644 index f37b976fba..0000000000 --- a/compiler/rustc_middle/src/hir/exports.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::ty; - -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::Res; -use rustc_hir::def_id::LocalDefId; -use rustc_macros::HashStable; -use rustc_span::symbol::Ident; -use rustc_span::Span; - -use std::fmt::Debug; - -/// This is the replacement export map. It maps a module to all of the exports -/// within. -pub type ExportMap = FxHashMap>; - -#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct Export { - /// The name of the target. - pub ident: Ident, - /// The resolution of the target. - /// Local variables cannot be exported, so this `Res` doesn't need the ID parameter. - pub res: Res, - /// The span of the target. - pub span: Span, - /// The visibility of the export. - /// We include non-`pub` exports for hygienic macros that get used from extern crates. - pub vis: ty::Visibility, -} diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 394a1fc227..f36847c778 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -12,6 +12,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::*; use rustc_index::vec::Idx; +use rustc_middle::hir::nested_filter; use rustc_span::def_id::StableCrateId; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::Spanned; @@ -117,13 +118,13 @@ pub struct ParentOwnerIterator<'hir> { } impl<'hir> Iterator for ParentOwnerIterator<'hir> { - type Item = (HirId, OwnerNode<'hir>); + type Item = (LocalDefId, OwnerNode<'hir>); fn next(&mut self) -> Option { if self.current_id.local_id.index() != 0 { self.current_id.local_id = ItemLocalId::new(0); if let Some(node) = self.map.tcx.hir_owner(self.current_id.owner) { - return Some((self.current_id, node.node)); + return Some((self.current_id.owner, node.node)); } } if self.current_id == CRATE_HIR_ID { @@ -141,42 +142,42 @@ impl<'hir> Iterator for ParentOwnerIterator<'hir> { // If this `HirId` doesn't have an entry, skip it and look for its `parent_id`. if let Some(node) = self.map.tcx.hir_owner(self.current_id.owner) { - return Some((self.current_id, node.node)); + return Some((self.current_id.owner, node.node)); } } } } impl<'hir> Map<'hir> { - pub fn krate(&self) -> &'hir Crate<'hir> { + pub fn krate(self) -> &'hir Crate<'hir> { self.tcx.hir_crate(()) } - pub fn root_module(&self) -> &'hir Mod<'hir> { + pub fn root_module(self) -> &'hir Mod<'hir> { match self.tcx.hir_owner(CRATE_DEF_ID).map(|o| o.node) { Some(OwnerNode::Crate(item)) => item, _ => bug!(), } } - pub fn items(&self) -> impl Iterator> + 'hir { + pub fn items(self) -> impl Iterator> + 'hir { let krate = self.krate(); - krate.owners.iter().filter_map(|owner| match owner.as_ref()?.node() { + krate.owners.iter().filter_map(|owner| match owner.as_owner()?.node() { OwnerNode::Item(item) => Some(item), _ => None, }) } - pub fn def_key(&self, def_id: LocalDefId) -> DefKey { + pub fn def_key(self, def_id: LocalDefId) -> DefKey { // Accessing the DefKey is ok, since it is part of DefPathHash. self.tcx.untracked_resolutions.definitions.def_key(def_id) } - pub fn def_path_from_hir_id(&self, id: HirId) -> Option { + pub fn def_path_from_hir_id(self, id: HirId) -> Option { self.opt_local_def_id(id).map(|def_id| self.def_path(def_id)) } - pub fn def_path(&self, def_id: LocalDefId) -> DefPath { + pub fn def_path(self, def_id: LocalDefId) -> DefPath { // Accessing the DefPath is ok, since it is part of DefPathHash. self.tcx.untracked_resolutions.definitions.def_path(def_id) } @@ -188,7 +189,7 @@ impl<'hir> Map<'hir> { } #[inline] - pub fn local_def_id(&self, hir_id: HirId) -> LocalDefId { + pub fn local_def_id(self, hir_id: HirId) -> LocalDefId { self.opt_local_def_id(hir_id).unwrap_or_else(|| { bug!( "local_def_id: no entry for `{:?}`, which has a map of `{:?}`", @@ -199,25 +200,32 @@ impl<'hir> Map<'hir> { } #[inline] - pub fn opt_local_def_id(&self, hir_id: HirId) -> Option { - // FIXME(#85914) is this access safe for incr. comp.? - self.tcx.untracked_resolutions.definitions.opt_hir_id_to_local_def_id(hir_id) + pub fn opt_local_def_id(self, hir_id: HirId) -> Option { + if hir_id.local_id == ItemLocalId::new(0) { + Some(hir_id.owner) + } else { + self.tcx + .hir_owner_nodes(hir_id.owner) + .as_owner()? + .local_id_to_def_id + .get(&hir_id.local_id) + .copied() + } } #[inline] - pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId { - // FIXME(#85914) is this access safe for incr. comp.? - self.tcx.untracked_resolutions.definitions.local_def_id_to_hir_id(def_id) + pub fn local_def_id_to_hir_id(self, def_id: LocalDefId) -> HirId { + self.tcx.local_def_id_to_hir_id(def_id) } - pub fn iter_local_def_id(&self) -> impl Iterator + '_ { + pub fn iter_local_def_id(self) -> impl Iterator + 'hir { // Create a dependency to the crate to be sure we reexcute this when the amount of // definitions change. self.tcx.ensure().hir_crate(()); self.tcx.untracked_resolutions.definitions.iter_local_def_id() } - pub fn opt_def_kind(&self, local_def_id: LocalDefId) -> Option { + pub fn opt_def_kind(self, local_def_id: LocalDefId) -> Option { let hir_id = self.local_def_id_to_hir_id(local_def_id); let def_kind = match self.find(hir_id)? { Node::Item(item) => match item.kind { @@ -304,49 +312,60 @@ impl<'hir> Map<'hir> { Some(def_kind) } - pub fn def_kind(&self, local_def_id: LocalDefId) -> DefKind { + 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)) } - pub fn find_parent_node(&self, id: HirId) -> Option { + pub fn find_parent_node(self, id: HirId) -> Option { if id.local_id == ItemLocalId::from_u32(0) { Some(self.tcx.hir_owner_parent(id.owner)) } else { - let owner = self.tcx.hir_owner_nodes(id.owner)?; + let owner = self.tcx.hir_owner_nodes(id.owner).as_owner()?; let node = owner.nodes[id.local_id].as_ref()?; let hir_id = HirId { owner: id.owner, local_id: node.parent }; Some(hir_id) } } - pub fn get_parent_node(&self, hir_id: HirId) -> HirId { + pub fn get_parent_node(self, hir_id: HirId) -> HirId { self.find_parent_node(hir_id).unwrap() } /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. - pub fn find(&self, id: HirId) -> Option> { + pub fn find(self, id: HirId) -> Option> { if id.local_id == ItemLocalId::from_u32(0) { let owner = self.tcx.hir_owner(id.owner)?; Some(owner.node.into()) } else { - let owner = self.tcx.hir_owner_nodes(id.owner)?; + let owner = self.tcx.hir_owner_nodes(id.owner).as_owner()?; let node = owner.nodes[id.local_id].as_ref()?; Some(node.node) } } + /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. + #[inline] + pub fn find_by_def_id(self, id: LocalDefId) -> Option> { + self.find(self.local_def_id_to_hir_id(id)) + } + /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. - pub fn get(&self, id: HirId) -> Node<'hir> { + 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)) } - pub fn get_if_local(&self, id: DefId) -> Option> { + /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. + #[inline] + pub fn get_by_def_id(self, id: LocalDefId) -> Node<'hir> { + self.find_by_def_id(id).unwrap_or_else(|| bug!("couldn't find {:?} in the HIR map", id)) + } + + pub fn get_if_local(self, id: DefId) -> Option> { id.as_local().and_then(|id| self.find(self.local_def_id_to_hir_id(id))) } - pub fn get_generics(&self, id: DefId) -> Option<&'hir Generics<'hir>> { - let id = id.as_local()?; + pub fn get_generics(self, id: LocalDefId) -> Option<&'hir Generics<'hir>> { let node = self.tcx.hir_owner(id)?; match node.node { OwnerNode::ImplItem(impl_item) => Some(&impl_item.generics), @@ -367,27 +386,27 @@ impl<'hir> Map<'hir> { } } - pub fn item(&self, id: ItemId) -> &'hir Item<'hir> { + pub fn item(self, id: ItemId) -> &'hir Item<'hir> { self.tcx.hir_owner(id.def_id).unwrap().node.expect_item() } - pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { + pub fn trait_item(self, id: TraitItemId) -> &'hir TraitItem<'hir> { self.tcx.hir_owner(id.def_id).unwrap().node.expect_trait_item() } - pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { + pub fn impl_item(self, id: ImplItemId) -> &'hir ImplItem<'hir> { self.tcx.hir_owner(id.def_id).unwrap().node.expect_impl_item() } - pub fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> { + pub fn foreign_item(self, id: ForeignItemId) -> &'hir ForeignItem<'hir> { self.tcx.hir_owner(id.def_id).unwrap().node.expect_foreign_item() } - pub fn body(&self, id: BodyId) -> &'hir Body<'hir> { + pub fn body(self, id: BodyId) -> &'hir Body<'hir> { self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies[&id.hir_id.local_id] } - pub fn fn_decl_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { + pub fn fn_decl_by_hir_id(self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { if let Some(node) = self.find(hir_id) { fn_decl(node) } else { @@ -395,7 +414,7 @@ impl<'hir> Map<'hir> { } } - pub fn fn_sig_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> { + pub fn fn_sig_by_hir_id(self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> { if let Some(node) = self.find(hir_id) { fn_sig(node) } else { @@ -403,7 +422,7 @@ impl<'hir> Map<'hir> { } } - pub fn enclosing_body_owner(&self, hir_id: HirId) -> HirId { + pub fn enclosing_body_owner(self, hir_id: HirId) -> HirId { for (parent, _) in self.parent_iter(hir_id) { if let Some(body) = self.maybe_body_owned_by(parent) { return self.body_owner(body); @@ -416,24 +435,24 @@ impl<'hir> Map<'hir> { /// Returns the `HirId` that corresponds to the definition of /// which this is the body of, i.e., a `fn`, `const` or `static` /// item (possibly associated), a closure, or a `hir::AnonConst`. - pub fn body_owner(&self, BodyId { hir_id }: BodyId) -> HirId { + pub fn body_owner(self, BodyId { hir_id }: BodyId) -> HirId { let parent = self.get_parent_node(hir_id); assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id))); parent } - pub fn body_owner_def_id(&self, id: BodyId) -> LocalDefId { + pub fn body_owner_def_id(self, id: BodyId) -> LocalDefId { self.local_def_id(self.body_owner(id)) } /// Given a `HirId`, returns the `BodyId` associated with it, /// if the node is a body owner, otherwise returns `None`. - pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option { + pub fn maybe_body_owned_by(self, hir_id: HirId) -> Option { self.find(hir_id).map(associated_body).flatten() } /// Given a body owner's id, returns the `BodyId` associated with it. - pub fn body_owned_by(&self, id: HirId) -> BodyId { + pub fn body_owned_by(self, id: HirId) -> BodyId { self.maybe_body_owned_by(id).unwrap_or_else(|| { span_bug!( self.span(id), @@ -443,7 +462,7 @@ impl<'hir> Map<'hir> { }) } - pub fn body_param_names(&self, id: BodyId) -> impl Iterator + '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::empty(), @@ -453,7 +472,7 @@ impl<'hir> Map<'hir> { /// Returns the `BodyOwnerKind` of this `LocalDefId`. /// /// Panics if `LocalDefId` does not have an associated body. - pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind { + pub fn body_owner_kind(self, id: HirId) -> BodyOwnerKind { match self.get(id) { Node::Item(&Item { kind: ItemKind::Const(..), .. }) | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. }) @@ -476,7 +495,7 @@ impl<'hir> Map<'hir> { /// This should only be used for determining the context of a body, a return /// value of `Some` does not always suggest that the owner of the body is `const`, /// just that it has to be checked as if it were. - pub fn body_const_context(&self, did: LocalDefId) -> Option { + pub fn body_const_context(self, did: LocalDefId) -> Option { let hir_id = self.local_def_id_to_hir_id(did); let ccx = match self.body_owner_kind(hir_id) { BodyOwnerKind::Const => ConstContext::Const, @@ -503,7 +522,7 @@ impl<'hir> Map<'hir> { .owners .iter_enumerated() .flat_map(move |(owner, owner_info)| { - let bodies = &owner_info.as_ref()?.nodes.bodies; + let bodies = &owner_info.as_owner()?.nodes.bodies; Some(bodies.iter().map(move |&(local_id, _)| { let hir_id = HirId { owner, local_id }; let body_id = BodyId { hir_id }; @@ -520,7 +539,7 @@ impl<'hir> Map<'hir> { par_iter(&self.krate().owners.raw).enumerate().for_each(|(owner, owner_info)| { let owner = LocalDefId::new(owner); - if let Some(owner_info) = owner_info { + if let MaybeOwner::Owner(owner_info) = owner_info { par_iter(owner_info.nodes.bodies.range(..)).for_each(|(local_id, _)| { let hir_id = HirId { owner, local_id: *local_id }; let body_id = BodyId { hir_id }; @@ -530,15 +549,17 @@ impl<'hir> Map<'hir> { }); } - pub fn ty_param_owner(&self, id: HirId) -> HirId { + pub fn ty_param_owner(self, id: HirId) -> LocalDefId { match self.get(id) { - Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => id, - Node::GenericParam(_) => self.get_parent_node(id), + Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => { + id.expect_owner() + } + Node::GenericParam(_) => self.get_parent_item(id), _ => bug!("ty_param_owner: {} not a type parameter", self.node_to_string(id)), } } - pub fn ty_param_name(&self, id: HirId) -> Symbol { + pub fn ty_param_name(self, id: HirId) -> Symbol { match self.get(id) { Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => { kw::SelfUpper @@ -548,18 +569,18 @@ impl<'hir> Map<'hir> { } } - pub fn trait_impls(&self, trait_did: DefId) -> &'hir [LocalDefId] { + pub fn trait_impls(self, trait_did: DefId) -> &'hir [LocalDefId] { self.tcx.all_local_trait_impls(()).get(&trait_did).map_or(&[], |xs| &xs[..]) } /// Gets the attributes on the crate. This is preferable to /// invoking `krate.attrs` because it registers a tighter /// dep-graph access. - pub fn krate_attrs(&self) -> &'hir [ast::Attribute] { + pub fn krate_attrs(self) -> &'hir [ast::Attribute] { self.attrs(CRATE_HIR_ID) } - pub fn get_module(&self, module: LocalDefId) -> (&'hir Mod<'hir>, Span, HirId) { + pub fn get_module(self, module: LocalDefId) -> (&'hir Mod<'hir>, Span, HirId) { let hir_id = HirId::make_owner(module); match self.tcx.hir_owner(module).map(|o| o.node) { Some(OwnerNode::Item(&Item { span, kind: ItemKind::Mod(ref m), .. })) => { @@ -580,7 +601,7 @@ impl<'hir> Map<'hir> { pub fn walk_attributes(self, visitor: &mut impl Visitor<'hir>) { let krate = self.krate(); for (owner, info) in krate.owners.iter_enumerated() { - if let Some(info) = info { + if let MaybeOwner::Owner(info) = info { for (local_id, attrs) in info.attrs.map.iter() { let id = HirId { owner, local_id: *local_id }; for a in *attrs { @@ -599,12 +620,12 @@ impl<'hir> Map<'hir> { /// follows lexical scoping rules -- then you want a different /// approach. You should override `visit_nested_item` in your /// visitor and then call `intravisit::walk_crate` instead. - pub fn visit_all_item_likes(&self, visitor: &mut V) + pub fn visit_all_item_likes(self, visitor: &mut V) where V: itemlikevisit::ItemLikeVisitor<'hir>, { let krate = self.krate(); - for owner in krate.owners.iter().filter_map(Option::as_ref) { + for owner in krate.owners.iter().filter_map(|i| i.as_owner()) { match owner.node() { OwnerNode::Item(item) => visitor.visit_item(item), OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item), @@ -616,21 +637,23 @@ impl<'hir> Map<'hir> { } /// A parallel version of `visit_all_item_likes`. - pub fn par_visit_all_item_likes(&self, visitor: &V) + pub fn par_visit_all_item_likes(self, visitor: &V) where V: itemlikevisit::ParItemLikeVisitor<'hir> + Sync + Send, { let krate = self.krate(); - par_for_each_in(&krate.owners.raw, |owner| match owner.as_ref().map(OwnerInfo::node) { - Some(OwnerNode::Item(item)) => visitor.visit_item(item), - Some(OwnerNode::ForeignItem(item)) => visitor.visit_foreign_item(item), - Some(OwnerNode::ImplItem(item)) => visitor.visit_impl_item(item), - Some(OwnerNode::TraitItem(item)) => visitor.visit_trait_item(item), - Some(OwnerNode::Crate(_)) | None => {} + par_for_each_in(&krate.owners.raw, |owner| match owner.map(OwnerInfo::node) { + MaybeOwner::Owner(OwnerNode::Item(item)) => visitor.visit_item(item), + MaybeOwner::Owner(OwnerNode::ForeignItem(item)) => visitor.visit_foreign_item(item), + MaybeOwner::Owner(OwnerNode::ImplItem(item)) => visitor.visit_impl_item(item), + MaybeOwner::Owner(OwnerNode::TraitItem(item)) => visitor.visit_trait_item(item), + MaybeOwner::Owner(OwnerNode::Crate(_)) + | MaybeOwner::NonOwner(_) + | MaybeOwner::Phantom => {} }) } - pub fn visit_item_likes_in_module(&self, module: LocalDefId, visitor: &mut V) + pub fn visit_item_likes_in_module(self, module: LocalDefId, visitor: &mut V) where V: ItemLikeVisitor<'hir>, { @@ -653,7 +676,7 @@ impl<'hir> Map<'hir> { } } - pub fn for_each_module(&self, f: impl Fn(LocalDefId)) { + pub fn for_each_module(self, f: impl Fn(LocalDefId)) { let mut queue = VecDeque::new(); queue.push_back(CRATE_DEF_ID); @@ -666,12 +689,12 @@ impl<'hir> Map<'hir> { #[cfg(not(parallel_compiler))] #[inline] - pub fn par_for_each_module(&self, f: impl Fn(LocalDefId)) { + pub fn par_for_each_module(self, f: impl Fn(LocalDefId)) { self.for_each_module(f) } #[cfg(parallel_compiler)] - pub fn par_for_each_module(&self, f: impl Fn(LocalDefId) + Sync) { + pub fn par_for_each_module(self, f: impl Fn(LocalDefId) + Sync) { use rustc_data_structures::sync::{par_iter, ParallelIterator}; par_iter_submodules(self.tcx, CRATE_DEF_ID, &f); @@ -698,7 +721,7 @@ impl<'hir> Map<'hir> { } /// Checks if the node is left-hand side of an assignment. - pub fn is_lhs(&self, id: HirId) -> bool { + 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, @@ -710,7 +733,7 @@ impl<'hir> Map<'hir> { /// 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 { + pub fn is_inside_const_context(self, hir_id: HirId) -> bool { self.body_const_context(self.local_def_id(self.enclosing_body_owner(hir_id))).is_some() } @@ -736,7 +759,7 @@ impl<'hir> Map<'hir> { /// false /// } /// ``` - pub fn get_return_block(&self, id: HirId) -> Option { + pub fn get_return_block(self, id: HirId) -> Option { let mut iter = self.parent_iter(id).peekable(); let mut ignore_tail = false; if let Some(node) = self.find(id) { @@ -776,23 +799,23 @@ impl<'hir> Map<'hir> { /// parent item is in this map. The "parent item" is the closest parent node /// in the HIR which is recorded by the map and is an item, either an item /// in a module, trait, or impl. - pub fn get_parent_item(&self, hir_id: HirId) -> HirId { - if let Some((hir_id, _node)) = self.parent_owner_iter(hir_id).next() { - hir_id + pub fn get_parent_item(self, hir_id: HirId) -> LocalDefId { + if let Some((def_id, _node)) = self.parent_owner_iter(hir_id).next() { + def_id } else { - CRATE_HIR_ID + CRATE_DEF_ID } } /// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no /// module parent is in this map. - pub(super) fn get_module_parent_node(&self, hir_id: HirId) -> HirId { - for (hir_id, node) in self.parent_owner_iter(hir_id) { + pub(super) fn get_module_parent_node(self, hir_id: HirId) -> LocalDefId { + for (def_id, node) in self.parent_owner_iter(hir_id) { if let OwnerNode::Item(&Item { kind: ItemKind::Mod(_), .. }) = node { - return hir_id; + return def_id; } } - CRATE_HIR_ID + CRATE_DEF_ID } /// When on an if expression, a match arm tail expression or a match arm, give back @@ -800,7 +823,7 @@ impl<'hir> Map<'hir> { /// /// 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_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(_) @@ -818,7 +841,7 @@ impl<'hir> Map<'hir> { } /// Returns the nearest enclosing scope. A scope is roughly an item or block. - pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option { + pub fn get_enclosing_scope(self, hir_id: HirId) -> Option { for (hir_id, node) in self.parent_iter(hir_id) { if let Node::Item(Item { kind: @@ -845,7 +868,7 @@ impl<'hir> Map<'hir> { } /// Returns the defining scope for an opaque type definition. - pub fn get_defining_scope(&self, id: HirId) -> HirId { + pub fn get_defining_scope(self, id: HirId) -> HirId { let mut scope = id; loop { scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); @@ -855,50 +878,49 @@ impl<'hir> Map<'hir> { } } - pub fn get_parent_did(&self, id: HirId) -> LocalDefId { - self.local_def_id(self.get_parent_item(id)) - } - - pub fn get_foreign_abi(&self, hir_id: HirId) -> Abi { + pub fn get_foreign_abi(self, hir_id: HirId) -> Abi { let parent = self.get_parent_item(hir_id); - if let Some(node) = self.tcx.hir_owner(self.local_def_id(parent)) { + if let Some(node) = self.tcx.hir_owner(parent) { if let OwnerNode::Item(Item { kind: ItemKind::ForeignMod { abi, .. }, .. }) = node.node { return *abi; } } - bug!("expected foreign mod or inlined parent, found {}", self.node_to_string(parent)) + bug!( + "expected foreign mod or inlined parent, found {}", + self.node_to_string(HirId::make_owner(parent)) + ) } - pub fn expect_item(&self, id: LocalDefId) -> &'hir Item<'hir> { + pub fn expect_item(self, id: LocalDefId) -> &'hir Item<'hir> { match self.tcx.hir_owner(id) { Some(Owner { node: OwnerNode::Item(item), .. }) => item, _ => bug!("expected item, found {}", self.node_to_string(HirId::make_owner(id))), } } - pub fn expect_impl_item(&self, id: LocalDefId) -> &'hir ImplItem<'hir> { + pub fn expect_impl_item(self, id: LocalDefId) -> &'hir ImplItem<'hir> { match self.tcx.hir_owner(id) { Some(Owner { node: OwnerNode::ImplItem(item), .. }) => item, _ => bug!("expected impl item, found {}", self.node_to_string(HirId::make_owner(id))), } } - pub fn expect_trait_item(&self, id: LocalDefId) -> &'hir TraitItem<'hir> { + pub fn expect_trait_item(self, id: LocalDefId) -> &'hir TraitItem<'hir> { match self.tcx.hir_owner(id) { Some(Owner { node: OwnerNode::TraitItem(item), .. }) => item, _ => bug!("expected trait item, found {}", self.node_to_string(HirId::make_owner(id))), } } - pub fn expect_variant(&self, id: HirId) -> &'hir Variant<'hir> { + pub fn expect_variant(self, id: HirId) -> &'hir Variant<'hir> { match self.find(id) { Some(Node::Variant(variant)) => variant, _ => bug!("expected variant, found {}", self.node_to_string(id)), } } - pub fn expect_foreign_item(&self, id: LocalDefId) -> &'hir ForeignItem<'hir> { + pub fn expect_foreign_item(self, id: LocalDefId) -> &'hir ForeignItem<'hir> { match self.tcx.hir_owner(id) { Some(Owner { node: OwnerNode::ForeignItem(item), .. }) => item, _ => { @@ -907,14 +929,14 @@ impl<'hir> Map<'hir> { } } - pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> { + pub fn expect_expr(self, id: HirId) -> &'hir Expr<'hir> { match self.find(id) { Some(Node::Expr(expr)) => expr, _ => bug!("expected expr, found {}", self.node_to_string(id)), } } - pub fn opt_name(&self, id: HirId) -> Option { + pub fn opt_name(self, id: HirId) -> Option { Some(match self.get(id) { Node::Item(i) => i.ident.name, Node::ForeignItem(fi) => fi.ident.name, @@ -925,12 +947,12 @@ impl<'hir> Map<'hir> { Node::Lifetime(lt) => lt.name.ident().name, 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::Ctor(..) => self.name(HirId::make_owner(self.get_parent_item(id))), _ => return None, }) } - pub fn name(&self, id: HirId) -> Symbol { + pub fn name(self, id: HirId) -> Symbol { match self.opt_name(id) { Some(name) => name, None => bug!("no name for {}", self.node_to_string(id)), @@ -939,18 +961,18 @@ 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] { + pub fn attrs(self, id: HirId) -> &'hir [ast::Attribute] { self.tcx.hir_attrs(id.owner).get(id.local_id) } /// 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 { + pub fn span(self, hir_id: HirId) -> Span { 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 { + pub fn opt_span(self, hir_id: HirId) -> Option { let span = match self.find(hir_id)? { Node::Param(param) => param.span, Node::Item(item) => match &item.kind { @@ -999,7 +1021,7 @@ impl<'hir> Map<'hir> { /// Like `hir.span()`, but includes the body of function items /// (instead of just the function header) - pub fn span_with_body(&self, hir_id: HirId) -> Span { + pub fn span_with_body(self, hir_id: HirId) -> Span { match self.find(hir_id) { Some(Node::TraitItem(item)) => item.span, Some(Node::ImplItem(impl_item)) => impl_item.span, @@ -1009,11 +1031,11 @@ impl<'hir> Map<'hir> { } } - pub fn span_if_local(&self, id: DefId) -> Option { + pub fn span_if_local(self, id: DefId) -> Option { 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 { + pub fn res_span(self, res: Res) -> Option { match res { Res::Err => None, Res::Local(id) => Some(self.span(id)), @@ -1023,13 +1045,13 @@ impl<'hir> Map<'hir> { /// Get a representation of this `id` for debugging purposes. /// NOTE: Do NOT use this in diagnostics! - pub fn node_to_string(&self, id: HirId) -> String { + pub fn node_to_string(self, id: HirId) -> String { hir_id_to_string(self, id) } /// Returns the HirId of `N` in `struct Foo` when /// called with the HirId for the `{ ... }` anon const - pub fn opt_const_param_default_param_hir_id(&self, anon_const: HirId) -> Option { + pub fn opt_const_param_default_param_hir_id(self, anon_const: HirId) -> Option { match self.get(self.get_parent_node(anon_const)) { Node::GenericParam(GenericParam { hir_id: param_id, @@ -1043,27 +1065,27 @@ impl<'hir> Map<'hir> { impl<'hir> intravisit::Map<'hir> for Map<'hir> { fn find(&self, hir_id: HirId) -> Option> { - self.find(hir_id) + (*self).find(hir_id) } fn body(&self, id: BodyId) -> &'hir Body<'hir> { - self.body(id) + (*self).body(id) } fn item(&self, id: ItemId) -> &'hir Item<'hir> { - self.item(id) + (*self).item(id) } fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { - self.trait_item(id) + (*self).trait_item(id) } fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { - self.impl_item(id) + (*self).impl_item(id) } fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> { - self.foreign_item(id) + (*self).foreign_item(id) } } @@ -1101,7 +1123,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh { .owners .iter_enumerated() .filter_map(|(def_id, info)| { - let _ = info.as_ref()?; + let _ = info.as_owner()?; let def_path_hash = definitions.def_path_hash(def_id); let span = definitions.def_span(def_id); debug_assert_eq!(span.parent(), None); @@ -1132,7 +1154,7 @@ fn upstream_crates(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> { upstream_crates } -fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String { +fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { let id_str = format!(" (hir_id={})", id); let path_str = || { @@ -1256,10 +1278,10 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalDefId) -> Module } impl<'hir> Visitor<'hir> for ModuleCollector<'hir> { - type Map = Map<'hir>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'hir Item<'hir>) { diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 95d7273b17..1053f0cefb 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -2,8 +2,8 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html -pub mod exports; pub mod map; +pub mod nested_filter; pub mod place; use crate::ty::query::Providers; @@ -59,26 +59,33 @@ impl<'tcx> TyCtxt<'tcx> { pub fn provide(providers: &mut Providers) { providers.parent_module_from_def_id = |tcx, id| { let hir = tcx.hir(); - hir.local_def_id(hir.get_module_parent_node(hir.local_def_id_to_hir_id(id))) + hir.get_module_parent_node(hir.local_def_id_to_hir_id(id)) }; providers.hir_crate = |tcx, ()| tcx.untracked_crate; providers.crate_hash = map::crate_hash; providers.hir_module_items = map::hir_module_items; providers.hir_owner = |tcx, id| { - let owner = tcx.hir_crate(()).owners[id].as_ref()?; + let owner = tcx.hir_crate(()).owners.get(id)?.as_owner()?; let node = owner.node(); Some(Owner { node, hash_without_bodies: owner.nodes.hash_without_bodies }) }; - providers.hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners[id].as_ref().map(|i| &i.nodes); + providers.local_def_id_to_hir_id = |tcx, id| { + let owner = tcx.hir_crate(()).owners[id].map(|_| ()); + match owner { + MaybeOwner::Owner(_) => HirId::make_owner(id), + MaybeOwner::Phantom => bug!("No HirId for {:?}", id), + MaybeOwner::NonOwner(hir_id) => hir_id, + } + }; + providers.hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners[id].map(|i| &i.nodes); providers.hir_owner_parent = |tcx, id| { // Accessing the def_key is ok since its value is hashed as part of `id`'s DefPathHash. let parent = tcx.untracked_resolutions.definitions.def_key(id).parent; let parent = parent.map_or(CRATE_HIR_ID, |local_def_index| { let def_id = LocalDefId { local_def_index }; - let mut parent_hir_id = - tcx.untracked_resolutions.definitions.local_def_id_to_hir_id(def_id); + let mut parent_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); if let Some(local_id) = - tcx.hir_crate(()).owners[parent_hir_id.owner].as_ref().unwrap().parenting.get(&id) + tcx.hir_crate(()).owners[parent_hir_id.owner].unwrap().parenting.get(&id) { parent_hir_id.local_id = *local_id; } @@ -87,7 +94,7 @@ pub fn provide(providers: &mut Providers) { parent }; providers.hir_attrs = - |tcx, id| tcx.hir_crate(()).owners[id].as_ref().map_or(AttributeMap::EMPTY, |o| &o.attrs); + |tcx, id| tcx.hir_crate(()).owners[id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs); providers.source_span = |tcx, def_id| tcx.resolutions(()).definitions.def_span(def_id); providers.def_span = |tcx, def_id| tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP); providers.fn_arg_names = |tcx, id| { @@ -111,4 +118,6 @@ pub fn provide(providers: &mut Providers) { let id = id.expect_local(); tcx.resolutions(()).definitions.expansion_that_defined(id) }; + providers.in_scope_traits_map = + |tcx, id| tcx.hir_crate(()).owners[id].as_owner().map(|owner_info| &owner_info.trait_map); } diff --git a/compiler/rustc_middle/src/hir/nested_filter.rs b/compiler/rustc_middle/src/hir/nested_filter.rs new file mode 100644 index 0000000000..7cfb207457 --- /dev/null +++ b/compiler/rustc_middle/src/hir/nested_filter.rs @@ -0,0 +1,27 @@ +use rustc_hir::intravisit::nested_filter::NestedFilter; + +/// Do not visit nested item-like things, but visit nested things +/// that are inside of an item-like. +/// +/// **This is the most common choice.** A very common pattern is +/// to use `visit_all_item_likes()` as an outer loop, +/// and to have the visitor that visits the contents of each item +/// using this setting. +pub struct OnlyBodies(()); +impl<'hir> NestedFilter<'hir> for OnlyBodies { + type Map = crate::hir::map::Map<'hir>; + const INTER: bool = false; + const INTRA: bool = true; +} + +/// Visits all nested things, including item-likes. +/// +/// **This is an unusual choice.** It is used when you want to +/// process everything within their lexical context. Typically you +/// kick off the visit by doing `walk_krate()`. +pub struct All(()); +impl<'hir> NestedFilter<'hir> for All { + type Map = crate::hir::map::Map<'hir>; + const INTER: bool = true; + const INTRA: bool = true; +} diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 605fff671d..419ed42924 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -23,7 +23,7 @@ use crate::infer::MemberConstraint; use crate::ty::subst::GenericArg; -use crate::ty::{self, BoundVar, List, Region, TyCtxt}; +use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt}; use rustc_index::vec::IndexVec; use rustc_macros::HashStable; use smallvec::SmallVec; @@ -64,9 +64,9 @@ pub struct CanonicalVarValues<'tcx> { /// result. #[derive(Clone, Debug)] pub struct OriginalQueryValues<'tcx> { - /// Map from the universes that appear in the query to the - /// universes in the caller context. For the time being, we only - /// ever put ROOT values into the query, so this map is very + /// Map from the universes that appear in the query to the universes in the + /// caller context. For all queries except `evaluate_goal` (used by Chalk), + /// we only ever put ROOT values into the query, so this map is very /// simple. pub universe_map: SmallVec<[ty::UniverseIndex; 4]>, @@ -104,7 +104,7 @@ impl<'tcx> CanonicalVarInfo<'tcx> { CanonicalVarKind::PlaceholderTy(_) => false, CanonicalVarKind::Region(_) => true, CanonicalVarKind::PlaceholderRegion(..) => false, - CanonicalVarKind::Const(_) => true, + CanonicalVarKind::Const(..) => true, CanonicalVarKind::PlaceholderConst(_) => false, } } @@ -130,7 +130,7 @@ pub enum CanonicalVarKind<'tcx> { PlaceholderRegion(ty::PlaceholderRegion), /// Some kind of const inference variable. - Const(ty::UniverseIndex), + Const(ty::UniverseIndex, Ty<'tcx>), /// A "placeholder" that represents "any const". PlaceholderConst(ty::PlaceholderConst<'tcx>), @@ -147,7 +147,7 @@ impl<'tcx> CanonicalVarKind<'tcx> { CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe, CanonicalVarKind::Region(ui) => ui, CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe, - CanonicalVarKind::Const(ui) => ui, + CanonicalVarKind::Const(ui, _) => ui, CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe, } } @@ -328,8 +328,8 @@ impl<'tcx> CanonicalVarValues<'tcx> { tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into() } GenericArgKind::Const(ct) => tcx - .mk_const(ty::Const { - ty: ct.ty, + .mk_const(ty::ConstS { + ty: ct.ty(), val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i)), }) .into(), diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index dcc49a5357..dd303aaada 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -32,9 +32,11 @@ impl<'tcx> From for RegionVidKey<'tcx> { impl<'tcx> UnifyKey for RegionVidKey<'tcx> { type Value = UnifiedRegion<'tcx>; + #[inline] fn index(&self) -> u32 { self.vid.as_u32() } + #[inline] fn from_index(i: u32) -> Self { RegionVidKey::from(ty::RegionVid::from_u32(i)) } @@ -95,14 +97,14 @@ pub enum ConstVariableOriginKind { #[derive(Copy, Clone, Debug)] pub enum ConstVariableValue<'tcx> { - Known { value: &'tcx ty::Const<'tcx> }, + Known { value: ty::Const<'tcx> }, Unknown { universe: ty::UniverseIndex }, } impl<'tcx> ConstVariableValue<'tcx> { /// If this value is known, returns the const it is known to be. /// Otherwise, `None`. - pub fn known(&self) -> Option<&'tcx ty::Const<'tcx>> { + pub fn known(&self) -> Option> { match *self { ConstVariableValue::Unknown { .. } => None, ConstVariableValue::Known { value } => Some(value), @@ -118,9 +120,11 @@ pub struct ConstVarValue<'tcx> { impl<'tcx> UnifyKey for ty::ConstVid<'tcx> { type Value = ConstVarValue<'tcx>; + #[inline] fn index(&self) -> u32 { self.index } + #[inline] fn from_index(i: u32) -> Self { ty::ConstVid { index: i, phantom: PhantomData } } @@ -130,7 +134,7 @@ impl<'tcx> UnifyKey for ty::ConstVid<'tcx> { } impl<'tcx> UnifyValue for ConstVarValue<'tcx> { - type Error = (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>); + type Error = (ty::Const<'tcx>, ty::Const<'tcx>); fn unify_values(&value1: &Self, &value2: &Self) -> Result { Ok(match (value1.val, value2.val) { @@ -162,18 +166,18 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> { } } -impl<'tcx> EqUnifyValue for &'tcx ty::Const<'tcx> {} +impl<'tcx> EqUnifyValue for ty::Const<'tcx> {} pub fn replace_if_possible<'tcx, V, L>( table: &mut UnificationTable, V, L>>, - c: &'tcx ty::Const<'tcx>, -) -> &'tcx ty::Const<'tcx> + c: ty::Const<'tcx>, +) -> ty::Const<'tcx> where V: snapshot_vec::VecLike>>, L: UndoLogs>>>, { - if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = c { - match table.probe_value(*vid).val.known() { + if let ty::ConstKind::Infer(InferConst::Var(vid)) = c.val() { + match table.probe_value(vid).val.known() { Some(c) => c, None => c, } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e6dd4e484c..e85cb413de 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -56,6 +56,7 @@ #![feature(nonzero_ops)] #![feature(unwrap_infallible)] #![recursion_limit = "512"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate bitflags; @@ -84,6 +85,7 @@ pub mod dep_graph; pub mod hir; pub mod infer; pub mod lint; +pub mod metadata; pub mod middle; pub mod mir; pub mod thir; diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index eef10356ed..17c77c1bbd 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -221,7 +221,6 @@ pub fn struct_lint_level<'s, 'd>( decorate: Box FnOnce(LintDiagnosticBuilder<'b>) + 'd>, ) { // Check for future incompatibility lints and issue a stronger warning. - let lint_id = LintId::of(lint); let future_incompatible = lint.future_incompatible; let has_future_breakage = future_incompatible.map_or( @@ -262,7 +261,7 @@ pub fn struct_lint_level<'s, 'd>( if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) { // Any suggestions made here are likely to be incorrect, so anything we // emit shouldn't be automatically fixed by rustfix. - err.allow_suggestions(false); + err.disable_suggestions(); // If this is a future incompatible that is not an edition fixing lint // it'll become a hard error, so we have to emit *something*. Also, @@ -345,31 +344,29 @@ pub fn struct_lint_level<'s, 'd>( err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn }); if let Some(future_incompatible) = future_incompatible { - let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) { - "once this associated item is added to the standard library, the ambiguity may \ - cause an error or change in behavior!" - .to_owned() - } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) { - "this borrowing pattern was not meant to be accepted, and may become a hard error \ - in the future" - .to_owned() - } else if let FutureIncompatibilityReason::EditionError(edition) = - future_incompatible.reason - { - let current_edition = sess.edition(); - format!( - "this is accepted in the current edition (Rust {}) but is a hard error in Rust {}!", - current_edition, edition - ) - } else if let FutureIncompatibilityReason::EditionSemanticsChange(edition) = - future_incompatible.reason - { - format!("this changes meaning in Rust {}", edition) - } else { - "this was previously accepted by the compiler but is being phased out; \ - it will become a hard error in a future release!" - .to_owned() + let explanation = match future_incompatible.reason { + FutureIncompatibilityReason::FutureReleaseError + | FutureIncompatibilityReason::FutureReleaseErrorReportNow => { + "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error in a future release!" + .to_owned() + } + FutureIncompatibilityReason::FutureReleaseSemanticsChange => { + "this will change its meaning in a future release!".to_owned() + } + FutureIncompatibilityReason::EditionError(edition) => { + let current_edition = sess.edition(); + format!( + "this is accepted in the current edition (Rust {}) but is a hard error in Rust {}!", + current_edition, edition + ) + } + FutureIncompatibilityReason::EditionSemanticsChange(edition) => { + format!("this changes meaning in Rust {}", edition) + } + FutureIncompatibilityReason::Custom(reason) => reason.to_owned(), }; + if future_incompatible.explain_reason { err.warn(&explanation); } diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs new file mode 100644 index 0000000000..6dcdc58c72 --- /dev/null +++ b/compiler/rustc_middle/src/metadata.rs @@ -0,0 +1,24 @@ +use crate::ty; + +use rustc_hir::def::Res; +use rustc_macros::HashStable; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +/// This structure is supposed to keep enough data to re-create `NameBinding`s for other crates +/// during name resolution. Right now the bindings are not recreated entirely precisely so we may +/// need to add more data in the future to correctly support macros 2.0, for example. +/// Module child can be either a proper item or a reexport (including private imports). +/// In case of reexport all the fields describe the reexport item itself, not what it refers to. +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct ModChild { + /// Name of the item. + pub ident: Ident, + /// Resolution result corresponding to the item. + /// Local variables cannot be exported, so this `Res` doesn't need the ID parameter. + pub res: Res, + /// Visibility of the item. + pub vis: ty::Visibility, + /// Span of the item. + pub span: Span, +} diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index b054d21ada..54eb2dc9e2 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -89,6 +89,8 @@ bitflags! { /// the MIR `InstrumentCoverage` pass and not added to the coverage map /// during codegen. const NO_COVERAGE = 1 << 15; + /// `#[used(linker)]`: indicates that LLVM nor the linker can eliminate this function. + const USED_LINKER = 1 << 16; } } diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index f33bd3438b..ff993ac392 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -28,7 +28,7 @@ pub enum AccessLevel { } /// Holds a map of accessibility levels for reachable HIR nodes. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AccessLevels { pub map: FxHashMap, } diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 39ca41c92f..75dd223d01 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -308,7 +308,7 @@ pub struct ScopeTree { /// The reason is that semantically, until the `box` expression returns, /// the values are still owned by their containing expressions. So /// we'll see that `&x`. - pub yield_in_scope: FxHashMap, + pub yield_in_scope: FxHashMap>, /// The number of visit_expr and visit_pat calls done in the body. /// Used to sanity check visit_expr/visit_pat call count when @@ -423,8 +423,8 @@ impl ScopeTree { /// Checks whether the given scope contains a `yield`. If so, /// returns `Some(YieldData)`. If not, returns `None`. - pub fn yield_in_scope(&self, scope: Scope) -> Option { - self.yield_in_scope.get(&scope).cloned() + pub fn yield_in_scope(&self, scope: Scope) -> Option<&Vec> { + self.yield_in_scope.get(&scope) } /// Gives the number of expressions visited in a body. diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 175d31d69d..fedf456ccc 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -29,7 +29,7 @@ pub enum StabilityLevel { } /// An entry in the `depr_map`. -#[derive(Clone, HashStable, Debug)] +#[derive(Copy, Clone, HashStable, Debug)] pub struct DeprecationEntry { /// The metadata of the attribute associated with this entry. pub attr: Deprecation, @@ -198,7 +198,7 @@ fn deprecation_message( } else { let since = since.as_ref().map(Symbol::as_str); - if since.as_deref() == Some("TBD") { + if since == Some("TBD") { format!("use of {} `{}` that will be deprecated in a future Rust version", kind, path) } else { format!( @@ -348,7 +348,7 @@ impl<'tcx> TyCtxt<'tcx> { // Deprecated attributes apply in-crate and cross-crate. if let Some(id) = id { if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { - let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id)); + let parent_def_id = self.hir().get_parent_item(id); let skip = self .lookup_deprecation_entry(parent_def_id.to_def_id()) .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); diff --git a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs index 5f028975bd..e2f3d6e078 100644 --- a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs +++ b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs @@ -45,8 +45,9 @@ impl serialize::Encodable for GraphIsCyclicCache { impl serialize::Decodable for GraphIsCyclicCache { #[inline] - fn decode(d: &mut D) -> Result { - serialize::Decodable::decode(d).map(|_v: ()| Self::new()) + fn decode(d: &mut D) -> Self { + let () = serialize::Decodable::decode(d); + Self::new() } } diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index b762a10da8..66f2c6e78a 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -273,20 +273,20 @@ pub struct AllocDecodingSession<'s> { impl<'s> AllocDecodingSession<'s> { /// Decodes an `AllocId` in a thread-safe way. - pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> Result + pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> AllocId where D: TyDecoder<'tcx>, { // Read the index of the allocation. - let idx = usize::try_from(decoder.read_u32()?).unwrap(); + let idx = usize::try_from(decoder.read_u32()).unwrap(); let pos = usize::try_from(self.state.data_offsets[idx]).unwrap(); // Decode the `AllocDiscriminant` now so that we know if we have to reserve an // `AllocId`. let (alloc_kind, pos) = decoder.with_position(pos, |decoder| { - let alloc_kind = AllocDiscriminant::decode(decoder)?; - Ok((alloc_kind, decoder.position())) - })?; + let alloc_kind = AllocDiscriminant::decode(decoder); + (alloc_kind, decoder.position()) + }); // Check the decoding state to see if it's already decoded or if we should // decode it here. @@ -295,7 +295,7 @@ impl<'s> AllocDecodingSession<'s> { match *entry { State::Done(alloc_id) => { - return Ok(alloc_id); + return alloc_id; } ref mut entry @ State::Empty => { // We are allowed to decode. @@ -329,7 +329,7 @@ impl<'s> AllocDecodingSession<'s> { State::InProgress(ref mut sessions, alloc_id) => { if sessions.contains(&self.session_id) { // Don't recurse. - return Ok(alloc_id); + return alloc_id; } else { // Start decoding concurrently. sessions.insert(self.session_id); @@ -343,37 +343,37 @@ impl<'s> AllocDecodingSession<'s> { let alloc_id = decoder.with_position(pos, |decoder| { match alloc_kind { AllocDiscriminant::Alloc => { - let alloc = <&'tcx Allocation as Decodable<_>>::decode(decoder)?; + let alloc = <&'tcx Allocation as Decodable<_>>::decode(decoder); // We already have a reserved `AllocId`. let alloc_id = alloc_id.unwrap(); trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc); decoder.tcx().set_alloc_id_same_memory(alloc_id, alloc); - Ok(alloc_id) + alloc_id } AllocDiscriminant::Fn => { assert!(alloc_id.is_none()); trace!("creating fn alloc ID"); - let instance = ty::Instance::decode(decoder)?; + let instance = ty::Instance::decode(decoder); trace!("decoded fn alloc instance: {:?}", instance); let alloc_id = decoder.tcx().create_fn_alloc(instance); - Ok(alloc_id) + alloc_id } AllocDiscriminant::Static => { assert!(alloc_id.is_none()); trace!("creating extern static alloc ID"); - let did = >::decode(decoder)?; + let did = >::decode(decoder); trace!("decoded static def-ID: {:?}", did); let alloc_id = decoder.tcx().create_static_alloc(did); - Ok(alloc_id) + alloc_id } } - })?; + }); self.state.decoding_state[idx].with_lock(|entry| { *entry = State::Done(alloc_id); }); - Ok(alloc_id) + alloc_id } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index f983185563..4a57f483c7 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -38,7 +38,7 @@ impl<'tcx> TyCtxt<'tcx> { ct: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { - match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs(self)) { + match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted: ct.promoted }; self.const_eval_global_id(param_env, cid, span) @@ -98,4 +98,12 @@ impl<'tcx> TyCtxt<'tcx> { let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?; Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory()) } + + /// Destructure a constant ADT or array into its variant index and its field values. + pub fn destructure_const( + self, + param_env_and_val: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>, + ) -> mir::DestructuredConst<'tcx> { + self.try_destructure_const(param_env_and_val).unwrap() + } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 52ef380001..7e5f8018df 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -13,6 +13,7 @@ use crate::ty::subst::{Subst, SubstsRef}; use crate::ty::{self, List, Ty, TyCtxt}; use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex}; +use rustc_errors::ErrorReported; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::{self, GeneratorKind}; @@ -162,7 +163,7 @@ impl MirPhase { } /// Where a specific `mir::Body` comes from. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable)] pub struct MirSource<'tcx> { pub instance: InstanceDef<'tcx>, @@ -284,11 +285,12 @@ pub struct Body<'tcx> { predecessor_cache: PredecessorCache, is_cyclic: GraphIsCyclicCache, + + pub tainted_by_errors: Option, } impl<'tcx> Body<'tcx> { pub fn new( - tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, basic_blocks: IndexVec>, source_scopes: IndexVec>, @@ -298,6 +300,7 @@ impl<'tcx> Body<'tcx> { var_debug_info: Vec>, span: Span, generator_kind: Option, + tainted_by_errors: Option, ) -> Self { // We need `arg_count` locals, and one for the return place. assert!( @@ -330,8 +333,9 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, predecessor_cache: PredecessorCache::new(), is_cyclic: GraphIsCyclicCache::new(), + tainted_by_errors, }; - body.is_polymorphic = body.definitely_has_param_types_or_consts(tcx); + body.is_polymorphic = body.has_param_types_or_consts(); body } @@ -341,7 +345,7 @@ impl<'tcx> Body<'tcx> { /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different /// crate. pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { - Body { + let mut body = Body { phase: MirPhase::Build, source: MirSource::item(DefId::local(CRATE_DEF_INDEX)), basic_blocks, @@ -357,7 +361,10 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, predecessor_cache: PredecessorCache::new(), is_cyclic: GraphIsCyclicCache::new(), - } + tainted_by_errors: None, + }; + body.is_polymorphic = body.has_param_types_or_consts(); + body } #[inline] @@ -618,20 +625,20 @@ impl<'tcx, E: TyEncoder<'tcx>, T: Encodable> Encodable for ClearCrossCrate } impl<'tcx, D: TyDecoder<'tcx>, T: Decodable> Decodable for ClearCrossCrate { #[inline] - fn decode(d: &mut D) -> Result, D::Error> { + fn decode(d: &mut D) -> ClearCrossCrate { if D::CLEAR_CROSS_CRATE { - return Ok(ClearCrossCrate::Clear); + return ClearCrossCrate::Clear; } - let discr = u8::decode(d)?; + let discr = u8::decode(d); match discr { - TAG_CLEAR_CROSS_CRATE_CLEAR => Ok(ClearCrossCrate::Clear), + TAG_CLEAR_CROSS_CRATE_CLEAR => ClearCrossCrate::Clear, TAG_CLEAR_CROSS_CRATE_SET => { - let val = T::decode(d)?; - Ok(ClearCrossCrate::Set(val)) + let val = T::decode(d); + ClearCrossCrate::Set(val) } - tag => Err(d.error(&format!("Invalid tag for ClearCrossCrate: {:?}", tag))), + tag => panic!("Invalid tag for ClearCrossCrate: {:?}", tag), } } } @@ -893,7 +900,7 @@ pub struct LocalDecl<'tcx> { /// across a suspension point against the type components of the generator /// which type checking knows are live across a suspension point. We need to /// flag drop flags to avoid triggering this check as they are introduced - /// after typeck. + /// outside of type inference. /// /// This should be sound because the drop flags are fully algebraic, and /// therefore don't affect the auto-trait or outlives properties of the @@ -1254,17 +1261,7 @@ pub enum AssertKind { ResumedAfterPanic(GeneratorKind), } -#[derive( - Clone, - Debug, - PartialEq, - PartialOrd, - TyEncodable, - TyDecodable, - Hash, - HashStable, - TypeFoldable -)] +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum InlineAsmOperand<'tcx> { In { reg: InlineAsmRegOrRegClass, @@ -1565,10 +1562,6 @@ pub enum StatementKind<'tcx> { /// End the current live range for the storage of the local. StorageDead(Local), - /// Executes a piece of inline Assembly. Stored in a Box to keep the size - /// of `StatementKind` low. - LlvmInlineAsm(Box>), - /// Retag references in the given place, ensuring they got fresh tags. This is /// part of the Stacked Borrows model. These statements are currently only interpreted /// by miri and only generated when "-Z mir-emit-retag" is passed. @@ -1590,7 +1583,7 @@ pub enum StatementKind<'tcx> { /// - `Bivariant` -- no effect AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), - /// Marks the start of a "coverage region", injected with '-Zinstrument-coverage'. A + /// Marks the start of a "coverage region", injected with '-Cinstrument-coverage'. A /// `Coverage` statement carries metadata about the coverage region, used to inject a coverage /// map into the binary. If `Coverage::kind` is a `Counter`, the statement also generates /// executable code, to increment a counter variable at runtime, each time the code region is @@ -1655,7 +1648,7 @@ pub enum FakeReadCause { ForMatchedPlace(Option), /// A fake read of the RefWithinGuard version of a bind-by-value variable - /// in a match guard to ensure that it's value hasn't change by the time + /// in a match guard to ensure that its value hasn't change by the time /// we create the OutsideGuard version. ForGuardBinding, @@ -1688,13 +1681,6 @@ pub enum FakeReadCause { ForIndex, } -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] -pub struct LlvmInlineAsm<'tcx> { - pub asm: hir::LlvmInlineAsmInner, - pub outputs: Box<[Place<'tcx>]>, - pub inputs: Box<[(Span, Operand<'tcx>)]>, -} - impl Debug for Statement<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::StatementKind::*; @@ -1719,9 +1705,6 @@ impl Debug for Statement<'_> { SetDiscriminant { ref place, variant_index } => { write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) } - LlvmInlineAsm(ref asm) => { - write!(fmt, "llvm_asm!({:?} : {:?} : {:?})", asm.asm, asm.outputs, asm.inputs) - } AscribeUserType(box (ref place, ref c_ty), ref variance) => { write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) } @@ -1760,7 +1743,7 @@ pub struct CopyNonOverlapping<'tcx> { /// A path to a value; something that can be evaluated without /// changing or disturbing program state. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, HashStable)] pub struct Place<'tcx> { pub local: Local, @@ -2085,7 +2068,7 @@ pub struct SourceScopeLocalData { /// These are values that can appear inside an rvalue. They are intentionally /// limited to prevent rvalues from being nested in one another. -#[derive(Clone, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum Operand<'tcx> { /// Copy: The value must be available for use afterwards. /// @@ -2202,7 +2185,7 @@ pub enum Rvalue<'tcx> { Use(Operand<'tcx>), /// [x; 32] - Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>), + Repeat(Operand<'tcx>, ty::Const<'tcx>), /// &x or &mut x Ref(Region<'tcx>, BorrowKind, Place<'tcx>), @@ -2287,11 +2270,13 @@ pub enum BinOp { Mul, /// The `/` operator (division) /// - /// Division by zero is UB. + /// Division by zero is UB, because the compiler should have inserted checks + /// prior to this. Div, /// The `%` operator (modulus) /// - /// Using zero as the modulus (second operand) is UB. + /// Using zero as the modulus (second operand) is UB, because the compiler + /// should have inserted checks prior to this. Rem, /// The `^` operator (bitwise xor) BitXor, @@ -2352,7 +2337,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { match *self { Use(ref place) => write!(fmt, "{:?}", place), - Repeat(ref a, ref b) => { + Repeat(ref a, b) => { write!(fmt, "[{:?}; ", a)?; pretty_print_const(b, fmt, false)?; write!(fmt, "]") @@ -2439,7 +2424,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { CtorKind::Fictive => { let mut struct_fmt = fmt.debug_struct(&name); for (field, place) in iter::zip(&variant_def.fields, places) { - struct_fmt.field(field.ident.as_str(), place); + struct_fmt.field(field.name.as_str(), place); } struct_fmt.finish() } @@ -2449,7 +2434,6 @@ impl<'tcx> Debug for Rvalue<'tcx> { AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| { if let Some(def_id) = def_id.as_local() { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let name = if tcx.sess.opts.debugging_opts.span_free_formats { let substs = tcx.lift(substs).unwrap(); format!( @@ -2457,7 +2441,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { tcx.def_path_str_with_substs(def_id.to_def_id(), substs), ) } else { - let span = tcx.hir().span(hir_id); + let span = tcx.def_span(def_id); format!( "[closure@{}]", tcx.sess.source_map().span_to_diagnostic_string(span) @@ -2481,8 +2465,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| { if let Some(def_id) = def_id.as_local() { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let name = format!("[generator@{:?}]", tcx.hir().span(hir_id)); + let name = format!("[generator@{:?}]", tcx.def_span(def_id)); let mut struct_fmt = fmt.debug_struct(&name); // FIXME(project-rfc-2229#48): This should be a list of capture names/places @@ -2515,7 +2498,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { /// this does not necessarily mean that they are `==` in Rust. In /// particular, one must be wary of `NaN`! -#[derive(Clone, Copy, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] pub struct Constant<'tcx> { pub span: Span, @@ -2529,11 +2512,11 @@ pub struct Constant<'tcx> { pub literal: ConstantKind<'tcx>, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)] #[derive(Lift)] pub enum ConstantKind<'tcx> { /// This constant came from the type system - Ty(&'tcx ty::Const<'tcx>), + Ty(ty::Const<'tcx>), /// This constant cannot go back into the type system, as it represents /// something the type system cannot handle (e.g. pointers). Val(interpret::ConstValue<'tcx>, Ty<'tcx>), @@ -2541,7 +2524,7 @@ pub enum ConstantKind<'tcx> { impl<'tcx> Constant<'tcx> { pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { - match self.literal.const_for_ty()?.val.try_to_scalar() { + match self.literal.try_to_scalar() { Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) { GlobalAlloc::Static(def_id) => { assert!(!tcx.is_thread_local_static(def_id)); @@ -2558,33 +2541,33 @@ impl<'tcx> Constant<'tcx> { } } -impl<'tcx> From<&'tcx ty::Const<'tcx>> for ConstantKind<'tcx> { +impl<'tcx> From> for ConstantKind<'tcx> { #[inline] - fn from(ct: &'tcx ty::Const<'tcx>) -> Self { + fn from(ct: ty::Const<'tcx>) -> Self { Self::Ty(ct) } } impl<'tcx> ConstantKind<'tcx> { /// Returns `None` if the constant is not trivially safe for use in the type system. - pub fn const_for_ty(&self) -> Option<&'tcx ty::Const<'tcx>> { + pub fn const_for_ty(&self) -> Option> { match self { - ConstantKind::Ty(c) => Some(c), + ConstantKind::Ty(c) => Some(*c), ConstantKind::Val(..) => None, } } pub fn ty(&self) -> Ty<'tcx> { match self { - ConstantKind::Ty(c) => c.ty, - ConstantKind::Val(_, ty) => ty, + ConstantKind::Ty(c) => c.ty(), + ConstantKind::Val(_, ty) => *ty, } } #[inline] pub fn try_to_value(self) -> Option> { match self { - ConstantKind::Ty(c) => c.val.try_to_value(), + ConstantKind::Ty(c) => c.val().try_to_value(), ConstantKind::Val(val, _) => Some(val), } } @@ -2785,7 +2768,7 @@ impl UserTypeProjection { field: Field, ) -> Self { self.projs.push(ProjectionElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), + Some(adt_def.variants[variant_index].name), variant_index, )); self.projs.push(ProjectionElem::Field(field, ())); @@ -2848,7 +2831,7 @@ impl<'tcx> Display for ConstantKind<'tcx> { } fn pretty_print_const<'tcx>( - c: &ty::Const<'tcx>, + c: ty::Const<'tcx>, fmt: &mut Formatter<'_>, print_types: bool, ) -> fmt::Result { @@ -2964,7 +2947,7 @@ impl Location { let mut visited = FxHashSet::default(); while let Some(block) = queue.pop() { - // If we haven't visited this block before, then make sure we visit it's predecessors. + // If we haven't visited this block before, then make sure we visit its predecessors. if visited.insert(block) { queue.extend(predecessors[block].iter().cloned()); } else { diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 1422537cd5..892808386d 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -179,15 +179,11 @@ impl<'tcx> MonoItem<'tcx> { pub fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option { match *self { - MonoItem::Fn(Instance { def, .. }) => { - def.def_id().as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) - } - MonoItem::Static(def_id) => { - def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) - } - MonoItem::GlobalAsm(item_id) => Some(item_id.hir_id()), + MonoItem::Fn(Instance { def, .. }) => def.def_id().as_local(), + MonoItem::Static(def_id) => def_id.as_local(), + MonoItem::GlobalAsm(item_id) => Some(item_id.def_id), } - .map(|hir_id| tcx.hir().span(hir_id)) + .map(|def_id| tcx.def_span(def_id)) } // Only used by rustc_codegen_cranelift @@ -247,6 +243,9 @@ pub struct CodegenUnit<'tcx> { items: FxHashMap, (Linkage, Visibility)>, size_estimate: Option, primary: bool, + /// True if this is CGU is used to hold code coverage information for dead code, + /// false otherwise. + is_code_coverage_dead_code_cgu: bool, } /// Specifies the linkage type for a `MonoItem`. @@ -277,7 +276,13 @@ pub enum Visibility { impl<'tcx> CodegenUnit<'tcx> { #[inline] pub fn new(name: Symbol) -> CodegenUnit<'tcx> { - CodegenUnit { name, items: Default::default(), size_estimate: None, primary: false } + CodegenUnit { + name, + items: Default::default(), + size_estimate: None, + primary: false, + is_code_coverage_dead_code_cgu: false, + } } pub fn name(&self) -> Symbol { @@ -304,6 +309,15 @@ impl<'tcx> CodegenUnit<'tcx> { &mut self.items } + pub fn is_code_coverage_dead_code_cgu(&self) -> bool { + self.is_code_coverage_dead_code_cgu + } + + /// Marks this CGU as the one used to contain code coverage information for dead code. + pub fn make_code_coverage_dead_code_cgu(&mut self) { + self.is_code_coverage_dead_code_cgu = true; + } + pub fn mangle_name(human_readable_name: &str) -> String { // We generate a 80 bit hash from the name. This should be enough to // avoid collisions and is still reasonably short for filenames. @@ -404,9 +418,11 @@ impl<'a, 'tcx> HashStable> for CodegenUnit<'tcx> { // The size estimate is not relevant to the hash size_estimate: _, primary: _, + is_code_coverage_dead_code_cgu, } = *self; name.hash_stable(hcx, hasher); + is_code_coverage_dead_code_cgu.hash_stable(hcx, hasher); let mut items: Vec<(Fingerprint, _)> = items .iter() diff --git a/compiler/rustc_middle/src/mir/predecessors.rs b/compiler/rustc_middle/src/mir/predecessors.rs index fd6bb76dc4..2562baac91 100644 --- a/compiler/rustc_middle/src/mir/predecessors.rs +++ b/compiler/rustc_middle/src/mir/predecessors.rs @@ -57,14 +57,15 @@ impl PredecessorCache { impl serialize::Encodable for PredecessorCache { #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { - serialize::Encodable::encode(&(), s) + s.emit_unit() } } impl serialize::Decodable for PredecessorCache { #[inline] - fn decode(d: &mut D) -> Result { - serialize::Decodable::decode(d).map(|_v: ()| Self::new()) + fn decode(d: &mut D) -> Self { + let () = d.read_unit(); + Self::new() } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8cc705384b..784babffef 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -17,9 +17,8 @@ use rustc_middle::mir::interpret::{ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::MirSource; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt, TyS, TypeFoldable, TypeVisitor}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_target::abi::Size; -use std::ops::ControlFlow; const INDENT: &str = " "; /// Alignment for lining up comments following MIR statements @@ -427,12 +426,12 @@ impl<'tcx> ExtraComments<'tcx> { } } -fn use_verbose<'tcx>(ty: &&TyS<'tcx>, fn_def: bool) -> bool { - match ty.kind() { +fn use_verbose<'tcx>(ty: Ty<'tcx>, fn_def: bool) -> bool { + match *ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false, // Unit type ty::Tuple(g_args) if g_args.is_empty() => false, - ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty(), fn_def)), + ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg.expect_ty(), fn_def)), ty::Array(ty, _) => use_verbose(ty, fn_def), ty::FnDef(..) => fn_def, _ => true, @@ -443,7 +442,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { self.super_constant(constant, location); let Constant { span, user_ty, literal } = constant; - if use_verbose(&literal.ty(), true) { + if use_verbose(literal.ty(), true) { self.push("mir::Constant"); self.push(&format!( "+ span: {}", @@ -462,9 +461,10 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { } } - fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { + fn visit_const(&mut self, constant: ty::Const<'tcx>, _: Location) { self.super_const(constant); - let ty::Const { ty, val, .. } = constant; + let ty = constant.ty(); + let val = constant.val(); if use_verbose(ty, false) { self.push("ty::Const"); self.push(&format!("+ ty: {:?}", ty)); @@ -476,8 +476,8 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { ty::ConstKind::Unevaluated(uv) => format!( "Unevaluated({}, {:?}, {:?})", self.tcx.def_path_str(uv.def.did), - uv.substs(self.tcx), - uv.promoted + uv.substs, + uv.promoted, ), ty::ConstKind::Value(val) => format!("Value({:?})", val), ty::ConstKind::Error(_) => "Error".to_string(), @@ -668,6 +668,7 @@ pub fn write_allocations<'tcx>( fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator + '_ { alloc.relocations().values().map(|id| *id) } + fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator + '_ { match val { ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _size)) => { @@ -681,23 +682,29 @@ pub fn write_allocations<'tcx>( } } } + struct CollectAllocIds(BTreeSet); - impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds { - fn tcx_for_anon_const_substs(&self) -> Option> { - // `AllocId`s are only inside of `ConstKind::Value` which - // can't be part of the anon const default substs. - None - } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { - if let ty::ConstKind::Value(val) = c.val { + impl<'tcx> Visitor<'tcx> for CollectAllocIds { + fn visit_const(&mut self, c: ty::Const<'tcx>, _loc: Location) { + if let ty::ConstKind::Value(val) = c.val() { self.0.extend(alloc_ids_from_const(val)); } - c.super_visit_with(self) + } + + fn visit_constant(&mut self, c: &Constant<'tcx>, loc: Location) { + match c.literal { + ConstantKind::Ty(c) => self.visit_const(c, loc), + ConstantKind::Val(val, _) => { + self.0.extend(alloc_ids_from_const(val)); + } + } } } + let mut visitor = CollectAllocIds(Default::default()); - body.visit_with(&mut visitor); + visitor.visit_body(body); + // `seen` contains all seen allocations, including the ones we have *not* printed yet. // The protocol is to first `insert` into `seen`, and only if that returns `true` // then push to `todo`. diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index cb3f385095..fbd5a2d08a 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -214,6 +214,7 @@ pub struct BorrowCheckResult<'tcx> { pub concrete_opaque_types: VecMap, Ty<'tcx>>, pub closure_requirements: Option>, pub used_mut_upvars: SmallVec<[Field; 8]>, + pub tainted_by_errors: Option, } /// The result of the `mir_const_qualif` query. @@ -227,7 +228,7 @@ pub struct ConstQualifs { pub needs_drop: bool, pub needs_non_const_drop: bool, pub custom_eq: bool, - pub error_occured: Option, + pub tainted_by_errors: Option, } /// After we borrow check a closure, we are left with various @@ -340,7 +341,7 @@ pub enum ConstraintCategory { /// like `Foo { field: my_val }`) Usage, OpaqueType, - ClosureUpvar(hir::HirId), + ClosureUpvar(Field), /// A constraint from a user-written predicate /// with the provided span, written on the item @@ -362,7 +363,7 @@ pub enum ConstraintCategory { #[derive(TyEncodable, TyDecodable, HashStable)] pub enum ReturnConstraint { Normal, - ClosureUpvar(hir::HirId), + ClosureUpvar(Field), } /// The subject of a `ClosureOutlivesRequirement` -- that is, the thing @@ -386,11 +387,11 @@ pub enum ClosureOutlivesSubject<'tcx> { #[derive(Copy, Clone, Debug, HashStable)] pub struct DestructuredConst<'tcx> { pub variant: Option, - pub fields: &'tcx [&'tcx ty::Const<'tcx>], + pub fields: &'tcx [ty::Const<'tcx>], } /// Coverage information summarized from a MIR if instrumented for source code coverage (see -/// compiler option `-Zinstrument-coverage`). This information is generated by the +/// compiler option `-Cinstrument-coverage`). This information is generated by the /// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)] pub struct CoverageInfo { diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index 507f997198..965d30a7b9 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs @@ -230,7 +230,7 @@ where } /// Format a string showing the start line and column, and end line and column within a file. -pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> String { +pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String { let source_map = tcx.sess.source_map(); let start = source_map.lookup_char_pos(span.lo()); let end = source_map.lookup_char_pos(span.hi()); @@ -245,7 +245,6 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str { SetDiscriminant { .. } => "SetDiscriminant", StorageLive(..) => "StorageLive", StorageDead(..) => "StorageDead", - LlvmInlineAsm(..) => "LlvmInlineAsm", Retag(..) => "Retag", AscribeUserType(..) => "AscribeUserType", Coverage(..) => "Coverage", @@ -630,7 +629,7 @@ fn tooltip<'tcx>( let mut text = Vec::new(); text.push(format!("{}: {}:", spanview_id, &source_map.span_to_embeddable_string(span))); for statement in statements { - let source_range = source_range_no_file(tcx, &statement.source_info.span); + let source_range = source_range_no_file(tcx, statement.source_info.span); text.push(format!( "\n{}{}: {}: {:?}", TOOLTIP_INDENT, @@ -640,7 +639,7 @@ fn tooltip<'tcx>( )); } if let Some(term) = terminator { - let source_range = source_range_no_file(tcx, &term.source_info.span); + let source_range = source_range_no_file(tcx, term.source_info.span); text.push(format!( "\n{}{}: {}: {:?}", TOOLTIP_INDENT, @@ -665,9 +664,7 @@ fn trim_span_hi(span: Span, to_pos: BytePos) -> Span { } fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span { - let hir_id = - tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local")); - let fn_decl_span = tcx.hir().span(hir_id); + let fn_decl_span = tcx.def_span(def_id); if let Some(body_span) = hir_body(tcx, def_id).map(|hir_body| hir_body.value.span) { if fn_decl_span.ctxt() == body_span.ctxt() { fn_decl_span.to(body_span) } else { body_span } } else { diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index dc53dc8de9..302921cc4a 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -57,7 +57,7 @@ impl<'tcx> PlaceTy<'tcx> { /// `PlaceElem`, where we can just use the `Ty` that is already /// stored inline on field projection elems. pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { - self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty) + self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, &ty| ty) } /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` @@ -93,11 +93,11 @@ impl<'tcx> PlaceTy<'tcx> { ProjectionElem::Subslice { from, to, from_end } => { PlaceTy::from_ty(match self.ty.kind() { ty::Slice(..) => self.ty, - ty::Array(inner, _) if !from_end => tcx.mk_array(inner, (to - from) as u64), + ty::Array(inner, _) if !from_end => tcx.mk_array(*inner, (to - from) as u64), ty::Array(inner, size) if from_end => { let size = size.eval_usize(tcx, param_env); let len = size - (from as u64) - (to as u64); - tcx.mk_array(inner, len) + tcx.mk_array(*inner, len) } _ => bug!("cannot subslice non-array type: `{:?}`", self), }) diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 51e4afaf22..ae94bd121f 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -105,7 +105,7 @@ impl<'a> Iterator for SwitchTargetsIter<'a> { impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {} -#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, PartialOrd)] +#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] pub enum TerminatorKind<'tcx> { /// Block should have one successor in the graph; we jump there. Goto { target: BasicBlock }, @@ -430,7 +430,7 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn as_switch(&self) -> Option<(&Operand<'tcx>, Ty<'tcx>, &SwitchTargets)> { match self { TerminatorKind::SwitchInt { discr, switch_ty, targets } => { - Some((discr, switch_ty, targets)) + Some((discr, *switch_ty, targets)) } _ => None, } diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 8c930fd161..480f28620d 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -4,8 +4,9 @@ use super::*; /// Preorder traversal of a graph. /// -/// Preorder traversal is when each node is visited before any of its -/// successors +/// Preorder traversal is when each node is visited after at least one of its predecessors. If you +/// are familar with some basic graph theory, then this performs a depth first search and returns +/// nodes in order of discovery time. /// /// ```text /// @@ -82,8 +83,9 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> { /// Postorder traversal of a graph. /// -/// Postorder traversal is when each node is visited after all of its -/// successors, except when the successor is only reachable by a back-edge +/// Postorder traversal is when each node is visited after all of its successors, except when the +/// successor is only reachable by a back-edge. If you are familiar with some basic graph theory, +/// then this performs a depth first search and returns nodes in order of completion time. /// /// /// ```text diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index f301c68a7c..a618800cc1 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -194,13 +194,13 @@ macro_rules! make_mir_visitor { } fn visit_region(&mut self, - region: & $($mutability)? ty::Region<'tcx>, + region: $(& $mutability)? ty::Region<'tcx>, _: Location) { self.super_region(region); } fn visit_const(&mut self, - constant: & $($mutability)? &'tcx ty::Const<'tcx>, + constant: $(& $mutability)? ty::Const<'tcx>, _: Location) { self.super_const(constant); } @@ -242,7 +242,7 @@ macro_rules! make_mir_visitor { ) { let span = body.span; if let Some(gen) = &$($mutability)? body.generator { - if let Some(yield_ty) = &$($mutability)? gen.yield_ty { + if let Some(yield_ty) = $(& $mutability)? gen.yield_ty { self.visit_ty( yield_ty, TyContext::YieldTy(SourceInfo::outermost(span)) @@ -266,7 +266,7 @@ macro_rules! make_mir_visitor { } self.visit_ty( - &$($mutability)? body.return_ty(), + $(& $mutability)? body.return_ty(), TyContext::ReturnTy(SourceInfo::outermost(body.span)) ); @@ -355,7 +355,7 @@ macro_rules! make_mir_visitor { ty::InstanceDef::DropGlue(_def_id, Some(ty)) | ty::InstanceDef::CloneShim(_def_id, ty) => { // FIXME(eddyb) use a better `TyContext` here. - self.visit_ty(ty, TyContext::Location(location)); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } } self.visit_substs(callee_substs, location); @@ -408,19 +408,6 @@ macro_rules! make_mir_visitor { location ); } - StatementKind::LlvmInlineAsm(asm) => { - for output in & $($mutability)? asm.outputs[..] { - self.visit_place( - output, - PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput), - location - ); - } - for (span, input) in & $($mutability)? asm.inputs[..] { - self.visit_span(span); - self.visit_operand(input, location); - } - } StatementKind::Retag(kind, place) => { self.visit_retag(kind, place, location); } @@ -500,7 +487,7 @@ macro_rules! make_mir_visitor { targets: _ } => { self.visit_operand(discr, location); - self.visit_ty(switch_ty, TyContext::Location(location)); + self.visit_ty($(& $mutability)? *switch_ty, TyContext::Location(location)); } TerminatorKind::Drop { @@ -654,7 +641,7 @@ macro_rules! make_mir_visitor { Rvalue::ThreadLocalRef(_) => {} Rvalue::Ref(r, bk, path) => { - self.visit_region(r, location); + self.visit_region($(& $mutability)? *r, location); let ctx = match bk { BorrowKind::Shared => PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow @@ -693,7 +680,7 @@ macro_rules! make_mir_visitor { Rvalue::Cast(_cast_kind, operand, ty) => { self.visit_operand(operand, location); - self.visit_ty(ty, TyContext::Location(location)); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } Rvalue::BinaryOp(_bin_op, box(lhs, rhs)) @@ -715,14 +702,14 @@ macro_rules! make_mir_visitor { } Rvalue::NullaryOp(_op, ty) => { - self.visit_ty(ty, TyContext::Location(location)); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } Rvalue::Aggregate(kind, operands) => { let kind = &$($mutability)? **kind; match kind { AggregateKind::Array(ty) => { - self.visit_ty(ty, TyContext::Location(location)); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } AggregateKind::Tuple => { } @@ -757,7 +744,7 @@ macro_rules! make_mir_visitor { Rvalue::ShallowInitBox(operand, ty) => { self.visit_operand(operand, location); - self.visit_ty(ty, TyContext::Location(location)); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } } } @@ -828,7 +815,7 @@ macro_rules! make_mir_visitor { is_block_tail: _, } = local_decl; - self.visit_ty(ty, TyContext::LocalDecl { + self.visit_ty($(& $mutability)? *ty, TyContext::LocalDecl { local, source_info: *source_info, }); @@ -877,8 +864,8 @@ macro_rules! make_mir_visitor { self.visit_span(span); drop(user_ty); // no visit method for this match literal { - ConstantKind::Ty(ct) => self.visit_const(ct, location), - ConstantKind::Val(_, t) => self.visit_ty(t, TyContext::Location(location)), + ConstantKind::Ty(ct) => self.visit_const($(& $mutability)? *ct, location), + ConstantKind::Val(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)), } } @@ -907,16 +894,16 @@ macro_rules! make_mir_visitor { ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>, ) { self.visit_span(& $($mutability)? ty.span); - self.visit_ty(& $($mutability)? ty.inferred_ty, TyContext::UserTy(ty.span)); + self.visit_ty($(& $mutability)? ty.inferred_ty, TyContext::UserTy(ty.span)); } fn super_ty(&mut self, _ty: $(& $mutability)? Ty<'tcx>) { } - fn super_region(&mut self, _region: & $($mutability)? ty::Region<'tcx>) { + fn super_region(&mut self, _region: $(& $mutability)? ty::Region<'tcx>) { } - fn super_const(&mut self, _const: & $($mutability)? &'tcx ty::Const<'tcx>) { + fn super_const(&mut self, _const: $(& $mutability)? ty::Const<'tcx>) { } fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) { @@ -1178,10 +1165,6 @@ pub enum NonMutatingUseContext { pub enum MutatingUseContext { /// Appears as LHS of an assignment. Store, - /// Can often be treated as a `Store`, but needs to be separate because - /// ASM is allowed to read outputs as well, so a `Store`-`LlvmAsmOutput` sequence - /// cannot be simplified the way a `Store`-`Store` can be. - LlvmAsmOutput, /// Output operand of an inline assembly block. AsmOutput, /// Destination of a call. @@ -1271,7 +1254,6 @@ impl PlaceContext { PlaceContext::MutatingUse( MutatingUseContext::Store | MutatingUseContext::Call - | MutatingUseContext::LlvmAsmOutput | MutatingUseContext::AsmOutput, ) ) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b3db2e6340..43cfe6f3b8 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -56,6 +56,14 @@ rustc_queries! { desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } } + /// Gives access to the HIR ID for the given `LocalDefId` owner `key`. + /// + /// This can be conveniently accessed by methods on `tcx.hir()`. + /// Avoid calling this query directly. + query local_def_id_to_hir_id(key: LocalDefId) -> hir::HirId { + desc { |tcx| "HIR ID of `{}`", tcx.def_path_str(key.to_def_id()) } + } + /// Gives access to the HIR node's parent for the HIR owner `key`. /// /// This can be conveniently accessed by methods on `tcx.hir()`. @@ -68,7 +76,7 @@ rustc_queries! { /// /// This can be conveniently accessed by methods on `tcx.hir()`. /// Avoid calling this query directly. - query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> { + query hir_owner_nodes(key: LocalDefId) -> hir::MaybeOwner<&'tcx hir::OwnerNodes<'tcx>> { desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } } @@ -105,15 +113,11 @@ rustc_queries! { /// Given the def_id of a const-generic parameter, computes the associated default const /// parameter. e.g. `fn example` called on `N` would return `3`. - query const_param_default(param: DefId) -> &'tcx ty::Const<'tcx> { + query const_param_default(param: DefId) -> ty::Const<'tcx> { desc { |tcx| "compute const default for a given parameter `{}`", tcx.def_path_str(param) } separate_provide_extern } - query default_anon_const_substs(key: DefId) -> SubstsRef<'tcx> { - desc { |tcx| "computing the default generic arguments for `{}`", tcx.def_path_str(key) } - } - /// Records the type of every item. query type_of(key: DefId) -> Ty<'tcx> { desc { |tcx| @@ -211,7 +215,8 @@ rustc_queries! { desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } - query native_libraries(_: CrateNum) -> Lrc> { + query native_libraries(_: CrateNum) -> Vec { + storage(ArenaCacheSelector<'tcx>) desc { "looking up the native libraries of a linked crate" } separate_provide_extern } @@ -250,13 +255,14 @@ rustc_queries! { /// Create a THIR tree for debugging. query thir_tree(key: ty::WithOptConstParam) -> String { no_hash + storage(ArenaCacheSelector<'tcx>) desc { |tcx| "constructing THIR tree for `{}`", tcx.def_path_str(key.did.to_def_id()) } } /// 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(_: ()) -> FxHashSet { + query mir_keys(_: ()) -> rustc_data_structures::fx::FxIndexSet { storage(ArenaCacheSelector<'tcx>) desc { "getting a list of all mir_keys" } } @@ -364,6 +370,7 @@ rustc_queries! { query symbols_for_closure_captures( key: (LocalDefId, DefId) ) -> Vec { + storage(ArenaCacheSelector<'tcx>) desc { |tcx| "symbols for captures of closure `{}` in `{}`", tcx.def_path_str(key.1), @@ -380,22 +387,12 @@ rustc_queries! { } /// Returns coverage summary info for a function, after executing the `InstrumentCoverage` - /// MIR pass (assuming the -Zinstrument-coverage option is enabled). + /// MIR pass (assuming the -Cinstrument-coverage option is enabled). query coverageinfo(key: ty::InstanceDef<'tcx>) -> mir::CoverageInfo { desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key.def_id()) } storage(ArenaCacheSelector<'tcx>) } - /// 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() } - } - /// 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> { @@ -544,7 +541,7 @@ rustc_queries! { query adt_dtorck_constraint( key: DefId - ) -> Result, NoSolution> { + ) -> Result<&'tcx DtorckConstraint<'tcx>, NoSolution> { desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } } @@ -630,6 +627,32 @@ rustc_queries! { desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } } + /// Maps from associated items on a trait to the corresponding associated + /// item on the impl specified by `impl_id`. + /// + /// For example, with the following code + /// + /// ``` + /// struct Type {} + /// // DefId + /// trait Trait { // trait_id + /// fn f(); // trait_f + /// fn g() {} // trait_g + /// } + /// + /// impl Trait for Type { // impl_id + /// fn f() {} // impl_f + /// fn g() {} // impl_g + /// } + /// ``` + /// + /// The map returned for `tcx.impl_item_implementor_ids(impl_id)` would be + ///`{ trait_f: impl_f, trait_g: impl_g }` + query impl_item_implementor_ids(impl_id: DefId) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "comparing impl items against trait for {}", tcx.def_path_str(impl_id) } + } + /// 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> { @@ -738,6 +761,22 @@ rustc_queries! { desc { |tcx| "checking liveness of variables in {}", describe_as_module(key, tcx) } } + /// Return the live symbols in the crate for dead code check. + /// + /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone) and + /// their respective impl (i.e., part of the derive macro) + query live_symbols_and_ignored_derived_traits(_: ()) -> ( + FxHashSet, + FxHashMap> + ) { + storage(ArenaCacheSelector<'tcx>) + desc { "find live symbols in crate" } + } + + query check_mod_deathness(key: LocalDefId) -> () { + desc { |tcx| "checking deathness of variables 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) } } @@ -772,24 +811,11 @@ rustc_queries! { desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } cache_on_disk_if { true } load_cached(tcx, id) { - #[cfg(bootstrap)] - { - match match tcx.on_disk_cache().as_ref() { - Some(c) => c.try_load_query_result(*tcx, id), - None => None, - } { - Some(x) => Some(&*tcx.arena.alloc(x)), - None => None, - } - } - #[cfg(not(bootstrap))] - { - let typeck_results: Option> = tcx - .on_disk_cache().as_ref() - .and_then(|c| c.try_load_query_result(*tcx, id)); - - typeck_results.map(|x| &*tcx.arena.alloc(x)) - } + let typeck_results: Option> = tcx + .on_disk_cache().as_ref() + .and_then(|c| c.try_load_query_result(*tcx, id)); + + typeck_results.map(|x| &*tcx.arena.alloc(x)) } } @@ -898,10 +924,12 @@ rustc_queries! { } /// 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> { + /// field values or return `None` if constant is invalid. + /// + /// Use infallible `TyCtxt::destructure_const` when you know that constant is valid. + query try_destructure_const( + key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>> + ) -> Option> { desc { "destructure constant" } remap_env_constness } @@ -909,8 +937,8 @@ rustc_queries! { /// 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> { + key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>> + ) -> ty::Const<'tcx> { desc { "deref constant" } remap_env_constness } @@ -921,7 +949,7 @@ rustc_queries! { query lit_to_const( key: LitToConstInput<'tcx> - ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { + ) -> Result, LitToConstError> { desc { "converting literal to const" } } @@ -1019,6 +1047,7 @@ rustc_queries! { /// Gets the rendered value of the specified constant or associated constant. /// Used by rustdoc. query rendered_const(def_id: DefId) -> String { + storage(ArenaCacheSelector<'tcx>) desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -1068,7 +1097,7 @@ rustc_queries! { query codegen_fulfill_obligation( key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) - ) -> Result, ErrorReported> { + ) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorReported> { cache_on_disk_if { true } desc { |tcx| "checking if `{}` fulfills its obligations", @@ -1077,7 +1106,7 @@ rustc_queries! { } /// Return all `impl` blocks in the current crate. - query all_local_trait_impls(_: ()) -> &'tcx BTreeMap> { + query all_local_trait_impls(_: ()) -> &'tcx rustc_data_structures::fx::FxIndexMap> { desc { "local trait impls" } } @@ -1119,33 +1148,33 @@ rustc_queries! { desc { "computing whether `{}` is `Copy`", env.value } remap_env_constness } - /// Query backing `TyS::is_sized`. + /// Query backing `Ty::is_sized`. query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Sized`", env.value } remap_env_constness } - /// Query backing `TyS::is_freeze`. + /// Query backing `Ty::is_freeze`. query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is freeze", env.value } remap_env_constness } - /// Query backing `TyS::is_unpin`. + /// Query backing `Ty::is_unpin`. query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Unpin`", env.value } remap_env_constness } - /// Query backing `TyS::needs_drop`. + /// Query backing `Ty::needs_drop`. query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } remap_env_constness } - /// Query backing `TyS::has_significant_drop_raw`. + /// Query backing `Ty::has_significant_drop_raw`. query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` has a significant drop", env.value } remap_env_constness } - /// Query backing `TyS::is_structural_eq_shallow`. + /// Query backing `Ty::is_structural_eq_shallow`. /// /// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types /// correctly. @@ -1214,6 +1243,7 @@ rustc_queries! { } query dependency_formats(_: ()) -> Lrc { + storage(ArenaCacheSelector<'tcx>) desc { "get the linkage format of all dependencies" } } @@ -1274,8 +1304,8 @@ rustc_queries! { 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()) } + query module_reexports(def_id: LocalDefId) -> Option<&'tcx [ModChild]> { + desc { |tcx| "looking up reexports of module `{}`", tcx.def_path_str(def_id.to_def_id()) } } query impl_defaultness(def_id: DefId) -> hir::Defaultness { @@ -1346,13 +1376,15 @@ rustc_queries! { /// 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), - } - separate_provide_extern + -> Option<&'tcx FxHashMap, CrateNum>> + { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| + "collecting available upstream monomorphizations for `{}`", + tcx.def_path_str(def_id), } + separate_provide_extern + } /// Returns the upstream crate that exports drop-glue for the given /// type (`substs` is expected to be a single-item list containing the @@ -1373,7 +1405,8 @@ rustc_queries! { desc { "available upstream drop-glue for `{:?}`", substs } } - query foreign_modules(_: CrateNum) -> Lrc> { + query foreign_modules(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) desc { "looking up the foreign modules of a linked crate" } separate_provide_extern } @@ -1399,11 +1432,13 @@ rustc_queries! { separate_provide_extern } query extra_filename(_: CrateNum) -> String { + storage(ArenaCacheSelector<'tcx>) eval_always desc { "looking up the extra filename for a crate" } separate_provide_extern } query crate_extern_paths(_: CrateNum) -> Vec { + storage(ArenaCacheSelector<'tcx>) eval_always desc { "looking up the paths for extern crates" } separate_provide_extern @@ -1416,13 +1451,6 @@ rustc_queries! { separate_provide_extern } - /// 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" } - separate_provide_extern - } - query is_dllimport_foreign_item(def_id: DefId) -> bool { desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) } } @@ -1462,8 +1490,7 @@ rustc_queries! { /// for each parameter if a trait object were to be passed for that parameter. /// For example, for `struct Foo<'a, T, U>`, this would be `['static, 'static]`. /// For `struct Foo<'a, T: 'a, U>`, this would instead be `['a, 'static]`. - query object_lifetime_defaults_map(_: LocalDefId) - -> Option> { + query object_lifetime_defaults(_: LocalDefId) -> Option<&'tcx [ObjectLifetimeDefault]> { desc { "looking up lifetime defaults for a region on an item" } } query late_bound_vars_map(_: LocalDefId) @@ -1472,6 +1499,7 @@ rustc_queries! { } query lifetime_scope_map(_: LocalDefId) -> Option> { + storage(ArenaCacheSelector<'tcx>) desc { "finds the lifetime scope for an HirId of a PathSegment" } } @@ -1485,7 +1513,7 @@ rustc_queries! { /// check whether the forest is empty. query type_uninhabited_from( key: ty::ParamEnvAnd<'tcx, Ty<'tcx>> - ) -> ty::inhabitedness::DefIdForest { + ) -> ty::inhabitedness::DefIdForest<'tcx> { desc { "computing the inhabitedness of `{:?}`", key } remap_env_constness } @@ -1502,8 +1530,8 @@ rustc_queries! { desc { "fetching what a crate is named" } separate_provide_extern } - query item_children(def_id: DefId) -> &'tcx [Export] { - desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } + query module_children(def_id: DefId) -> &'tcx [ModChild] { + desc { |tcx| "collecting child items of module `{}`", tcx.def_path_str(def_id) } separate_provide_extern } query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option { @@ -1564,6 +1592,7 @@ rustc_queries! { separate_provide_extern } query used_crate_source(_: CrateNum) -> Lrc { + storage(ArenaCacheSelector<'tcx>) eval_always desc { "looking at the source for a crate" } separate_provide_extern @@ -1654,7 +1683,11 @@ rustc_queries! { desc { "optimization level used by backend" } } - query output_filenames(_: ()) -> Arc { + /// Return the filenames where output artefacts shall be stored. + /// + /// This query returns an `&Arc` because codegen backends need the value even after the `TyCtxt` + /// has been destroyed. + query output_filenames(_: ()) -> &'tcx Arc { eval_always desc { "output_filenames" } } @@ -1896,6 +1929,7 @@ rustc_queries! { /// all of the cases that the normal `ty::Ty`-based wfcheck does. This is fine, /// because the `ty::Ty`-based wfcheck is always run. query diagnostic_hir_wf_check(key: (ty::Predicate<'tcx>, traits::WellFormedLoc)) -> Option> { + storage(ArenaCacheSelector<'tcx>) eval_always no_hash desc { "performing HIR wf-checking for predicate {:?} at item {:?}", key.0, key.1 } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 8d6fd1e729..04bc0c8b52 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -17,6 +17,7 @@ use rustc_index::newtype_index; use rustc_index::vec::IndexVec; use rustc_middle::infer::canonical::Canonical; use rustc_middle::middle::region; +use rustc_middle::mir::interpret::AllocId; use rustc_middle::mir::{ BinOp, BorrowKind, FakeReadCause, Field, Mutability, UnOp, UserTypeProjection, }; @@ -213,7 +214,7 @@ pub struct Expr<'tcx> { #[derive(Debug, HashStable)] pub enum ExprKind<'tcx> { - /// `Scope`s are used to explicitely mark destruction scopes, + /// `Scope`s are used to explicitly mark destruction scopes, /// and to track the `HirId` of the expressions within the scope. Scope { region_scope: region::Scope, @@ -368,12 +369,12 @@ pub enum ExprKind<'tcx> { }, /// An inline `const` block, e.g. `const {}`. ConstBlock { - value: &'tcx Const<'tcx>, + value: Const<'tcx>, }, /// An array literal constructed from one repeated element, e.g. `[1; 5]`. Repeat { value: ExprId, - count: &'tcx Const<'tcx>, + count: Const<'tcx>, }, /// An array, e.g. `[a, b, c, d]`. Array { @@ -407,7 +408,7 @@ pub enum ExprKind<'tcx> { }, /// A literal. Literal { - literal: &'tcx Const<'tcx>, + literal: Const<'tcx>, user_ty: Option>>, /// The `DefId` of the `const` item this literal /// was produced from, if this is not a user-written @@ -419,7 +420,8 @@ pub enum ExprKind<'tcx> { /// This is only distinguished from `Literal` so that we can register some /// info for diagnostics. StaticRef { - literal: &'tcx Const<'tcx>, + alloc_id: AllocId, + ty: Ty<'tcx>, def_id: DefId, }, /// Inline assembly, i.e. `asm!()`. @@ -431,12 +433,6 @@ pub enum ExprKind<'tcx> { }, /// An expression taking a reference to a thread local. ThreadLocalRef(DefId), - /// Inline LLVM assembly, i.e. `llvm_asm!()`. - LlvmInlineAsm { - asm: &'tcx hir::LlvmInlineAsmInner, - outputs: Box<[ExprId]>, - inputs: Box<[ExprId]>, - }, /// A `yield` expression. Yield { value: ExprId, @@ -507,7 +503,7 @@ pub enum InlineAsmOperand<'tcx> { out_expr: Option, }, Const { - value: &'tcx Const<'tcx>, + value: Const<'tcx>, span: Span, }, SymFn { @@ -646,7 +642,7 @@ pub enum PatKind<'tcx> { /// * Opaque constants, that must not be matched structurally. So anything that does not derive /// `PartialEq` and `Eq`. Constant { - value: &'tcx ty::Const<'tcx>, + value: ty::Const<'tcx>, }, Range(PatRange<'tcx>), @@ -676,8 +672,8 @@ pub enum PatKind<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, HashStable)] pub struct PatRange<'tcx> { - pub lo: &'tcx ty::Const<'tcx>, - pub hi: &'tcx ty::Const<'tcx>, + pub lo: ty::Const<'tcx>, + pub hi: ty::Const<'tcx>, pub end: RangeEnd, } @@ -726,7 +722,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { }; if let Some(variant) = variant { - write!(f, "{}", variant.ident)?; + write!(f, "{}", variant.name)?; // Only for Adt we can have `S {...}`, // which we handle separately here. @@ -738,7 +734,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { if let PatKind::Wild = *p.pattern.kind { continue; } - let name = variant.fields[p.field.index()].ident; + let name = variant.fields[p.field.index()].name; write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; printed += 1; } diff --git a/compiler/rustc_middle/src/thir/abstract_const.rs b/compiler/rustc_middle/src/thir/abstract_const.rs index f80beadd6e..e3d004ed13 100644 --- a/compiler/rustc_middle/src/thir/abstract_const.rs +++ b/compiler/rustc_middle/src/thir/abstract_const.rs @@ -22,7 +22,7 @@ pub enum CastKind { /// A node of an `AbstractConst`. #[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] pub enum Node<'tcx> { - Leaf(&'tcx ty::Const<'tcx>), + Leaf(ty::Const<'tcx>), Binop(mir::BinOp, NodeId, NodeId), UnaryOp(mir::UnOp, NodeId), FunctionCall(NodeId, &'tcx [NodeId]), diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 7fc15e02fc..b3e2cb132a 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -26,7 +26,7 @@ pub trait Visitor<'a, 'tcx: 'a>: Sized { walk_pat(self, pat); } - fn visit_const(&mut self, _cnst: &'tcx Const<'tcx>) {} + fn visit_const(&mut self, _cnst: Const<'tcx>) {} } pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Expr<'tcx>) { @@ -123,7 +123,7 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp } Closure { closure_id: _, substs: _, upvars: _, movability: _, fake_reads: _ } => {} Literal { literal, user_ty: _, const_id: _ } => visitor.visit_const(literal), - StaticRef { literal, def_id: _ } => visitor.visit_const(literal), + StaticRef { .. } => {} InlineAsm { ref operands, template: _, options: _, line_spans: _ } => { for op in &**operands { use InlineAsmOperand::*; @@ -145,14 +145,6 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp } } ThreadLocalRef(_) => {} - LlvmInlineAsm { ref outputs, ref inputs, asm: _ } => { - for &out_expr in &**outputs { - visitor.visit_expr(&visitor.thir()[out_expr]); - } - for &in_expr in &**inputs { - visitor.visit_expr(&visitor.thir()[in_expr]); - } - } Yield { value } => visitor.visit_expr(&visitor.thir()[value]), } } @@ -217,7 +209,7 @@ pub fn walk_pat<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, pat: &Pat<' visitor.visit_pat(&subpattern.pattern); } } - Constant { value } => visitor.visit_const(value), + Constant { value } => visitor.visit_const(*value), Range(range) => { visitor.visit_const(range.lo); visitor.visit_const(range.hi); diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index de5beffb5c..b54418e520 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -285,6 +285,12 @@ pub enum ObligationCauseCode<'tcx> { trait_item_def_id: DefId, }, + /// Checking that the bounds of a trait's associated type hold for a given impl + CheckAssociatedTypeBounds { + impl_item_def_id: DefId, + trait_item_def_id: DefId, + }, + /// Checking that this expression can be assigned where it needs to be // FIXME(eddyb) #11161 is the original Expr required? ExprAssignable, @@ -402,7 +408,7 @@ impl ObligationCauseCode<'_> { // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(ObligationCauseCode<'_>, 40); +static_assert_size!(ObligationCauseCode<'_>, 48); #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum StatementAsExpression { @@ -440,11 +446,11 @@ pub struct IfExpressionCause { #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] pub struct DerivedObligationCause<'tcx> { - /// The trait reference of the parent obligation that led to the + /// The trait predicate of the parent obligation that led to the /// current obligation. Note that only trait obligations lead to - /// derived obligations, so we just store the trait reference here + /// derived obligations, so we just store the trait predicate here /// directly. - pub parent_trait_ref: ty::PolyTraitRef<'tcx>, + pub parent_trait_pred: ty::PolyTraitPredicate<'tcx>, /// The parent trait had this cause. pub parent_code: Lrc>, @@ -566,7 +572,7 @@ pub enum ImplSource<'tcx, N> { TraitAlias(ImplSourceTraitAliasData<'tcx, N>), /// ImplSource for a `const Drop` implementation. - ConstDrop(ImplSourceConstDropData), + ConstDrop(ImplSourceConstDropData), } impl<'tcx, N> ImplSource<'tcx, N> { @@ -581,10 +587,10 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Object(d) => d.nested, ImplSource::FnPointer(d) => d.nested, ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - | ImplSource::Pointee(ImplSourcePointeeData) - | ImplSource::ConstDrop(ImplSourceConstDropData) => Vec::new(), + | ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(), ImplSource::TraitAlias(d) => d.nested, ImplSource::TraitUpcasting(d) => d.nested, + ImplSource::ConstDrop(i) => i.nested, } } @@ -599,10 +605,10 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Object(d) => &d.nested, ImplSource::FnPointer(d) => &d.nested, ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - | ImplSource::Pointee(ImplSourcePointeeData) - | ImplSource::ConstDrop(ImplSourceConstDropData) => &[], + | ImplSource::Pointee(ImplSourcePointeeData) => &[], ImplSource::TraitAlias(d) => &d.nested, ImplSource::TraitUpcasting(d) => &d.nested, + ImplSource::ConstDrop(i) => &i.nested, } } @@ -661,9 +667,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { nested: d.nested.into_iter().map(f).collect(), }) } - ImplSource::ConstDrop(ImplSourceConstDropData) => { - ImplSource::ConstDrop(ImplSourceConstDropData) - } + ImplSource::ConstDrop(i) => ImplSource::ConstDrop(ImplSourceConstDropData { + nested: i.nested.into_iter().map(f).collect(), + }), } } } @@ -755,8 +761,10 @@ pub struct ImplSourceDiscriminantKindData; #[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub struct ImplSourcePointeeData; -#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] -pub struct ImplSourceConstDropData; +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] +pub struct ImplSourceConstDropData { + pub nested: Vec, +} #[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, TypeFoldable, Lift)] pub struct ImplSourceTraitAliasData<'tcx, N> { diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index cb35a4005f..07cfe83b01 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -11,7 +11,6 @@ use crate::ty::subst::GenericArg; use crate::ty::{self, Ty, TyCtxt}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; use rustc_errors::struct_span_err; use rustc_query_system::ich::StableHashingContext; use rustc_span::source_map::Span; @@ -97,7 +96,7 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> = pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; -#[derive(Clone, Debug, HashStable)] +#[derive(Copy, Clone, Debug, HashStable)] pub struct NoSolution; pub type Fallible = Result; @@ -191,12 +190,12 @@ pub struct CandidateStep<'tcx> { pub unsize: bool, } -#[derive(Clone, Debug, HashStable)] +#[derive(Copy, Clone, Debug, HashStable)] pub struct MethodAutoderefStepsResult<'tcx> { /// The valid autoderef steps that could be find. - pub steps: Lrc>>, + pub steps: &'tcx [CandidateStep<'tcx>], /// If Some(T), a type autoderef reported an error on. - pub opt_bad_ty: Option>>, + pub opt_bad_ty: Option<&'tcx MethodAutoderefBadTy<'tcx>>, /// If `true`, `steps` has been truncated due to reaching the /// recursion limit. pub reached_recursion_limit: bool, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 71ee00c602..e18f04d92e 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -146,8 +146,8 @@ pub enum SelectionCandidate<'tcx> { BuiltinUnsizeCandidate, - /// Implementation of `const Drop`. - ConstDropCandidate, + /// Implementation of `const Drop`, optionally from a custom `impl const Drop`. + ConstDropCandidate(Option), } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 3e9cd6b46b..03a6daaf8a 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -4,7 +4,7 @@ use crate::ty::{self, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorReported; use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_span::symbol::Ident; +use rustc_span::symbol::sym; /// A per-trait graph of impls in specialization order. At the moment, this /// graph forms a tree rooted with the trait itself, with all other nodes @@ -46,6 +46,41 @@ impl Graph { } } +/// What kind of overlap check are we doing -- this exists just for testing and feature-gating +/// purposes. +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable, Debug, TyEncodable, TyDecodable)] +pub enum OverlapMode { + /// The 1.0 rules (either types fail to unify, or where clauses are not implemented for crate-local types) + Stable, + /// Feature-gated test: Stable, *or* there is an explicit negative impl that rules out one of the where-clauses. + WithNegative, + /// Just check for negative impls, not for "where clause not implemented": used for testing. + Strict, +} + +impl OverlapMode { + pub fn get<'tcx>(tcx: TyCtxt<'tcx>, trait_id: DefId) -> OverlapMode { + let with_negative_coherence = tcx.features().with_negative_coherence; + let strict_coherence = tcx.has_attr(trait_id, sym::rustc_strict_coherence); + + if with_negative_coherence { + if strict_coherence { OverlapMode::Strict } else { OverlapMode::WithNegative } + } else if strict_coherence { + bug!("To use strict_coherence you need to set with_negative_coherence feature flag"); + } else { + OverlapMode::Stable + } + } + + pub fn use_negative_impl(&self) -> bool { + *self == OverlapMode::Strict || *self == OverlapMode::WithNegative + } + + pub fn use_implicit_negative(&self) -> bool { + *self == OverlapMode::Stable || *self == OverlapMode::WithNegative + } +} + /// Children of a given impl, grouped into blanket/non-blanket varieties as is /// done in `TraitDef`. #[derive(Default, TyEncodable, TyDecodable, Debug, HashStable)] @@ -75,34 +110,28 @@ pub enum Node { Trait(DefId), } -impl<'tcx> Node { +impl Node { pub fn is_from_trait(&self) -> bool { matches!(self, Node::Trait(..)) } - /// Iterate over the items defined directly by the given (impl or trait) node. - pub fn items(&self, tcx: TyCtxt<'tcx>) -> impl 'tcx + Iterator { - tcx.associated_items(self.def_id()).in_definition_order() - } - - /// Finds an associated item defined in this node. + /// Trys to find the associated item that implements `trait_item_def_id` + /// defined in this node. /// /// If this returns `None`, the item can potentially still be found in /// parents of this node. - pub fn item( + pub fn item<'tcx>( &self, tcx: TyCtxt<'tcx>, - trait_item_name: Ident, - trait_item_kind: ty::AssocKind, - trait_def_id: DefId, - ) -> Option { - tcx.associated_items(self.def_id()) - .filter_by_name_unhygienic(trait_item_name.name) - .find(move |impl_item| { - trait_item_kind == impl_item.kind - && tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id) - }) - .copied() + trait_item_def_id: DefId, + ) -> Option<&'tcx ty::AssocItem> { + match *self { + Node::Trait(_) => Some(tcx.associated_item(trait_item_def_id)), + Node::Impl(impl_def_id) => { + let id = tcx.impl_item_implementor_ids(impl_def_id).get(&trait_item_def_id)?; + Some(tcx.associated_item(*id)) + } + } } pub fn def_id(&self) -> DefId { @@ -181,17 +210,11 @@ impl LeafDef { impl<'tcx> Ancestors<'tcx> { /// Finds the bottom-most (ie. most specialized) definition of an associated /// item. - pub fn leaf_def( - mut self, - tcx: TyCtxt<'tcx>, - trait_item_name: Ident, - trait_item_kind: ty::AssocKind, - ) -> Option { - let trait_def_id = self.trait_def_id; + pub fn leaf_def(mut self, tcx: TyCtxt<'tcx>, trait_item_def_id: DefId) -> Option { let mut finalizing_node = None; self.find_map(|node| { - if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) { + if let Some(item) = node.item(tcx, trait_item_def_id) { if finalizing_node.is_none() { let is_specializable = item.defaultness.is_default() || tcx.impl_defaultness(node.def_id()).is_default(); @@ -201,7 +224,7 @@ impl<'tcx> Ancestors<'tcx> { } } - Some(LeafDef { item, defining_node: node, finalizing_node }) + Some(LeafDef { item: *item, defining_node: node, finalizing_node }) } else { // Item not mentioned. This "finalizes" any defaulted item provided by an ancestor. finalizing_node = Some(node); diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index aa2f37bd81..6ce9f5eea3 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -120,6 +120,12 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx, } } +impl fmt::Debug for traits::ImplSourceConstDropData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ImplSourceConstDropData(nested={:?})", self.nested) + } +} + /////////////////////////////////////////////////////////////////////////// // Lift implementations @@ -127,5 +133,4 @@ TrivialTypeFoldableAndLiftImpls! { super::IfExpressionCause, super::ImplSourceDiscriminantKindData, super::ImplSourcePointeeData, - super::ImplSourceConstDropData, } diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs index e0e3febe6b..738c48dbb5 100644 --- a/compiler/rustc_middle/src/ty/_match.rs +++ b/compiler/rustc_middle/src/ty/_match.rs @@ -77,7 +77,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { ) => Ok(a), (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - Err(TypeError::Sorts(relate::expected_found(self, &a, &b))) + Err(TypeError::Sorts(relate::expected_found(self, a, b))) } (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(self.tcx().ty_error()), @@ -88,21 +88,21 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { debug!("{}.consts({:?}, {:?})", self.tag(), a, b); if a == b { return Ok(a); } - match (a.val, b.val) { + match (a.val(), b.val()) { (_, ty::ConstKind::Infer(InferConst::Fresh(_))) => { return Ok(a); } (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { - return Err(TypeError::ConstMismatch(relate::expected_found(self, &a, &b))); + return Err(TypeError::ConstMismatch(relate::expected_found(self, a, b))); } _ => {} diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 5cde54c932..40fbea7c3d 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -4,6 +4,7 @@ use crate::ty::util::{Discr, IntTypeExt}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::ErrorReported; use rustc_hir as hir; @@ -25,7 +26,7 @@ use super::{ Destructor, FieldDef, GenericPredicates, ReprOptions, Ty, TyCtxt, VariantDef, VariantDiscr, }; -#[derive(Clone, HashStable, Debug)] +#[derive(Copy, Clone, HashStable, Debug)] pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); bitflags! { @@ -136,12 +137,13 @@ impl Hash for AdtDef { impl<'a> HashStable> for AdtDef { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { - static CACHE: RefCell> = Default::default(); + static CACHE: RefCell> = Default::default(); } let hash: Fingerprint = CACHE.with(|cache| { let addr = self as *const AdtDef as usize; - *cache.borrow_mut().entry(addr).or_insert_with(|| { + let hashing_controls = hcx.hashing_controls(); + *cache.borrow_mut().entry((addr, hashing_controls)).or_insert_with(|| { let ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; let mut hasher = StableHasher::new(); @@ -393,7 +395,7 @@ impl<'tcx> AdtDef { | Res::Def(DefKind::Union, _) | Res::Def(DefKind::TyAlias, _) | Res::Def(DefKind::AssocTy, _) - | Res::SelfTy(..) + | Res::SelfTy { .. } | Res::SelfCtor(..) => self.non_enum_variant(), _ => bug!("unexpected res {:?} in variant_of_res", res), } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index bf5a3e6825..49f846562a 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -40,22 +40,30 @@ impl AssocItemContainer { } } +/// Information about an associated item #[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash)] pub struct AssocItem { pub def_id: DefId, - #[stable_hasher(project(name))] - pub ident: Ident, + pub name: Symbol, pub kind: AssocKind, pub vis: Visibility, pub defaultness: hir::Defaultness, pub container: AssocItemContainer, + /// If this is an item in an impl of a trait then this is the `DefId` of + /// the associated item on the trait that this implements. + pub trait_item_def_id: Option, + /// Whether this is a method with an explicit self /// as its first parameter, allowing method calls. pub fn_has_self_parameter: bool, } impl AssocItem { + pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident { + Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap()) + } + pub fn signature(&self, tcx: TyCtxt<'_>) -> String { match self.kind { ty::AssocKind::Fn => { @@ -65,9 +73,9 @@ impl AssocItem { // regions just fine, showing `fn(&MyType)`. tcx.fn_sig(self.def_id).skip_binder().to_string() } - ty::AssocKind::Type => format!("type {};", self.ident), + ty::AssocKind::Type => format!("type {};", self.name), ty::AssocKind::Const => { - format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id)) + format!("const {}: {:?};", self.name, tcx.type_of(self.def_id)) } } } @@ -110,7 +118,7 @@ pub struct AssocItems<'tcx> { impl<'tcx> AssocItems<'tcx> { /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. pub fn new(items_in_def_order: impl IntoIterator) -> Self { - let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); + let items = items_in_def_order.into_iter().map(|item| (item.name, item)).collect(); AssocItems { items } } @@ -134,21 +142,6 @@ impl<'tcx> AssocItems<'tcx> { self.items.get_by_key(name).copied() } - /// Returns an iterator over all associated items with the given name. - /// - /// Multiple items may have the same name if they are in different `Namespace`s. For example, - /// an associated type can have the same name as a method. Use one of the `find_by_name_and_*` - /// methods below if you know which item you are looking for. - pub fn filter_by_name<'a>( - &'a self, - tcx: TyCtxt<'a>, - ident: Ident, - parent_def_id: DefId, - ) -> impl 'a + Iterator { - self.filter_by_name_unhygienic(ident.name) - .filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } - /// Returns the associated item with the given name and `AssocKind`, if one exists. pub fn find_by_name_and_kind( &self, @@ -159,7 +152,19 @@ impl<'tcx> AssocItems<'tcx> { ) -> Option<&ty::AssocItem> { self.filter_by_name_unhygienic(ident.name) .filter(|item| item.kind == kind) - .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id)) + } + + /// Returns the associated item with the given name and any of `AssocKind`, if one exists. + pub fn find_by_name_and_kinds( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + // Sorted in order of what kinds to look at + kinds: &[AssocKind], + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + kinds.iter().find_map(|kind| self.find_by_name_and_kind(tcx, ident, *kind, parent_def_id)) } /// Returns the associated item with the given name in the given `Namespace`, if one exists. @@ -172,6 +177,6 @@ impl<'tcx> AssocItems<'tcx> { ) -> Option<&ty::AssocItem> { self.filter_by_name_unhygienic(ident.name) .filter(|item| item.kind.namespace() == ns) - .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id)) } } diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 771acc2964..8ba6c1f67c 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -52,35 +52,18 @@ impl UpvarId { /// Information describing the capture of an upvar. This is computed /// during `typeck`, specifically by `regionck`. #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] -pub enum UpvarCapture<'tcx> { +pub enum UpvarCapture { /// Upvar is captured by value. This is always true when the /// closure is labeled `move`, but can also be true in other cases /// depending on inference. - /// - /// If the upvar was inferred to be captured by value (e.g. `move` - /// was not used), then the `Span` points to a usage that - /// required it. There may be more than one such usage - /// (e.g. `|| { a; a; }`), in which case we pick an - /// arbitrary one. - ByValue(Option), + ByValue, /// Upvar is captured by reference. - ByRef(UpvarBorrow<'tcx>), -} - -#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] -pub struct UpvarBorrow<'tcx> { - /// The kind of borrow: by-ref upvars have access to shared - /// immutable borrows, which are not part of the normal language - /// syntax. - pub kind: BorrowKind, - - /// Region of the resulting reference. - pub region: ty::Region<'tcx>, + ByRef(BorrowKind), } pub type UpvarListMap = FxHashMap>; -pub type UpvarCaptureMap<'tcx> = FxHashMap>; +pub type UpvarCaptureMap = FxHashMap; /// Given the closure DefId this map provides a map of root variables to minimum /// set of `CapturedPlace`s that need to be tracked to support all captures of that closure. @@ -133,7 +116,7 @@ impl<'tcx> ClosureKind { } /// Returns the representative scalar type for this closure kind. - /// See `TyS::to_opt_closure_kind` for more details. + /// See `Ty::to_opt_closure_kind` for more details. pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match self { ty::ClosureKind::Fn => tcx.types.i8, @@ -150,10 +133,13 @@ pub struct CapturedPlace<'tcx> { pub place: HirPlace<'tcx>, /// `CaptureKind` and expression(s) that resulted in such capture of `place`. - pub info: CaptureInfo<'tcx>, + pub info: CaptureInfo, /// Represents if `place` can be mutated or not. pub mutability: hir::Mutability, + + /// Region of the resulting reference if the upvar is captured by ref. + pub region: Option>, } impl<'tcx> CapturedPlace<'tcx> { @@ -178,7 +164,7 @@ impl<'tcx> CapturedPlace<'tcx> { write!( &mut symbol, "__{}", - def.variants[variant].fields[idx as usize].ident.name.as_str(), + def.variants[variant].fields[idx as usize].name.as_str(), ) .unwrap(); } @@ -287,7 +273,7 @@ pub fn is_ancestor_or_same_capture( /// for a particular capture as well as identifying the part of the source code /// that triggered this capture to occur. #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] -pub struct CaptureInfo<'tcx> { +pub struct CaptureInfo { /// Expr Id pointing to use that resulted in selecting the current capture kind /// /// Eg: @@ -325,7 +311,7 @@ pub struct CaptureInfo<'tcx> { pub path_expr_id: Option, /// Capture mode that was selected - pub capture_kind: UpvarCapture<'tcx>, + pub capture_kind: UpvarCapture, } pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { @@ -344,7 +330,7 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc curr_string = format!( "{}.{}", curr_string, - def.variants[variant].fields[idx as usize].ident.name.as_str() + def.variants[variant].fields[idx as usize].name.as_str() ); } ty::Tuple(_) => { diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index db37d98614..ecd30ba441 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -13,8 +13,9 @@ use crate::mir::{ interpret::{AllocId, Allocation}, }; use crate::thir; +use crate::traits; use crate::ty::subst::SubstsRef; -use crate::ty::{self, List, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::Span; @@ -71,7 +72,7 @@ pub trait TyEncoder<'tcx>: Encoder { /// `Decodable` can still be implemented in cases where `Decodable` is required /// by a trait bound. pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>> { - fn decode(d: &mut D) -> Result<&'tcx Self, D::Error>; + fn decode(d: &mut D) -> &'tcx Self; } /// Encode the given value or a previously cached shorthand. @@ -137,6 +138,18 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Predicate<'tcx> { } } +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Region<'tcx> { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + self.kind().encode(e) + } +} + +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Const<'tcx> { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + self.0.0.encode(e) + } +} + impl<'tcx, E: TyEncoder<'tcx>> Encodable for AllocId { fn encode(&self, e: &mut E) -> Result<(), E::Error> { e.encode_alloc_id(self) @@ -155,7 +168,7 @@ macro_rules! encodable_via_deref { encodable_via_deref! { &'tcx ty::TypeckResults<'tcx>, - ty::Region<'tcx>, + &'tcx traits::ImplSource<'tcx, ()>, &'tcx mir::Body<'tcx>, &'tcx mir::UnsafetyCheckResult, &'tcx mir::BorrowCheckResult<'tcx>, @@ -172,13 +185,9 @@ pub trait TyDecoder<'tcx>: Decoder { fn position(&self) -> usize; - fn cached_ty_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> + fn cached_ty_for_shorthand(&mut self, shorthand: usize, or_insert_with: F) -> Ty<'tcx> where - F: FnOnce(&mut Self) -> Result, Self::Error>; + F: FnOnce(&mut Self) -> Ty<'tcx>; fn with_position(&mut self, pos: usize, f: F) -> R where @@ -188,35 +197,35 @@ pub trait TyDecoder<'tcx>: Decoder { (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 } - fn decode_alloc_id(&mut self) -> Result; + fn decode_alloc_id(&mut self) -> AllocId; } #[inline] fn decode_arena_allocable<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( decoder: &mut D, -) -> Result<&'tcx T, D::Error> +) -> &'tcx T where D: TyDecoder<'tcx>, { - Ok(decoder.tcx().arena.alloc(Decodable::decode(decoder)?)) + decoder.tcx().arena.alloc(Decodable::decode(decoder)) } #[inline] fn decode_arena_allocable_slice<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( decoder: &mut D, -) -> Result<&'tcx [T], D::Error> +) -> &'tcx [T] where D: TyDecoder<'tcx>, { - Ok(decoder.tcx().arena.alloc_from_iter( as Decodable>::decode(decoder)?)) + decoder.tcx().arena.alloc_from_iter( as Decodable>::decode(decoder)) } impl<'tcx, D: TyDecoder<'tcx>> Decodable for Ty<'tcx> { #[allow(rustc::usage_of_ty_tykind)] - fn decode(decoder: &mut D) -> Result, D::Error> { + fn decode(decoder: &mut D) -> Ty<'tcx> { // Handle shorthands first, if we have a usize > 0x80. if decoder.positioned_at_shorthand() { - let pos = decoder.read_usize()?; + let pos = decoder.read_usize(); assert!(pos >= SHORTHAND_OFFSET); let shorthand = pos - SHORTHAND_OFFSET; @@ -225,87 +234,89 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for Ty<'tcx> { }) } else { let tcx = decoder.tcx(); - Ok(tcx.mk_ty(ty::TyKind::decode(decoder)?)) + tcx.mk_ty(ty::TyKind::decode(decoder)) } } } impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Binder<'tcx, ty::PredicateKind<'tcx>> { - fn decode(decoder: &mut D) -> Result>, D::Error> { - let bound_vars = Decodable::decode(decoder)?; + fn decode(decoder: &mut D) -> ty::Binder<'tcx, ty::PredicateKind<'tcx>> { + let bound_vars = Decodable::decode(decoder); // Handle shorthands first, if we have a usize > 0x80. - Ok(ty::Binder::bind_with_vars( + ty::Binder::bind_with_vars( if decoder.positioned_at_shorthand() { - let pos = decoder.read_usize()?; + 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) }, bound_vars, - )) + ) } } 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) + fn decode(decoder: &mut D) -> ty::Predicate<'tcx> { + let predicate_kind = Decodable::decode(decoder); + decoder.tcx().mk_predicate(predicate_kind) } } impl<'tcx, D: TyDecoder<'tcx>> Decodable for SubstsRef<'tcx> { - fn decode(decoder: &mut D) -> Result { - let len = decoder.read_usize()?; + fn decode(decoder: &mut D) -> Self { + let len = decoder.read_usize(); let tcx = decoder.tcx(); - tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder))) + tcx.mk_substs( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) } } impl<'tcx, D: TyDecoder<'tcx>> Decodable for mir::Place<'tcx> { - fn decode(decoder: &mut D) -> Result { - let local: mir::Local = Decodable::decode(decoder)?; - let len = decoder.read_usize()?; - let projection: &'tcx List> = - decoder.tcx().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?; - Ok(mir::Place { local, projection }) + fn decode(decoder: &mut D) -> Self { + let local: mir::Local = Decodable::decode(decoder); + let len = decoder.read_usize(); + let projection = decoder.tcx().mk_place_elems( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ); + mir::Place { local, projection } } } impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Region<'tcx> { - fn decode(decoder: &mut D) -> Result { - Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?)) + fn decode(decoder: &mut D) -> Self { + decoder.tcx().mk_region(Decodable::decode(decoder)) } } impl<'tcx, D: TyDecoder<'tcx>> Decodable for CanonicalVarInfos<'tcx> { - fn decode(decoder: &mut D) -> Result { - let len = decoder.read_usize()?; - let interned: Result>, _> = + fn decode(decoder: &mut D) -> Self { + let len = decoder.read_usize(); + let interned: Vec> = (0..len).map(|_| Decodable::decode(decoder)).collect(); - Ok(decoder.tcx().intern_canonical_var_infos(interned?.as_slice())) + decoder.tcx().intern_canonical_var_infos(interned.as_slice()) } } impl<'tcx, D: TyDecoder<'tcx>> Decodable for AllocId { - fn decode(decoder: &mut D) -> Result { + fn decode(decoder: &mut D) -> Self { decoder.decode_alloc_id() } } impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::SymbolName<'tcx> { - fn decode(decoder: &mut D) -> Result { - Ok(ty::SymbolName::new(decoder.tcx(), &decoder.read_str()?)) + fn decode(decoder: &mut D) -> Self { + ty::SymbolName::new(decoder.tcx(), &decoder.read_str()) } } macro_rules! impl_decodable_via_ref { ($($t:ty),+) => { $(impl<'tcx, D: TyDecoder<'tcx>> Decodable for $t { - fn decode(decoder: &mut D) -> Result { + fn decode(decoder: &mut D) -> Self { RefDecodable::decode(decoder) } })* @@ -313,77 +324,73 @@ macro_rules! impl_decodable_via_ref { } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - let len = decoder.read_usize()?; - decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder))) + fn decode(decoder: &mut D) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.tcx().mk_type_list((0..len).map::, _>(|_| Decodable::decode(decoder))) } } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List>> { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - let len = decoder.read_usize()?; - decoder.tcx().mk_poly_existential_predicates((0..len).map(|_| Decodable::decode(decoder))) + fn decode(decoder: &mut D) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.tcx().mk_poly_existential_predicates( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) } } -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::Const<'tcx> { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?)) +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Const<'tcx> { + fn decode(decoder: &mut D) -> Self { + decoder.tcx().mk_const(Decodable::decode(decoder)) } } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - Ok(decoder.tcx().arena.alloc_from_iter( - (0..decoder.read_usize()?) - .map(|_| Decodable::decode(decoder)) - .collect::, _>>()?, - )) + fn decode(decoder: &mut D) -> &'tcx Self { + decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::>(), + ) } } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for Allocation { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?)) + fn decode(decoder: &mut D) -> &'tcx Self { + decoder.tcx().intern_const_alloc(Decodable::decode(decoder)) } } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [(ty::Predicate<'tcx>, Span)] { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - Ok(decoder.tcx().arena.alloc_from_iter( - (0..decoder.read_usize()?) - .map(|_| Decodable::decode(decoder)) - .collect::, _>>()?, - )) + fn decode(decoder: &mut D) -> &'tcx Self { + decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::>(), + ) } } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [thir::abstract_const::Node<'tcx>] { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - Ok(decoder.tcx().arena.alloc_from_iter( - (0..decoder.read_usize()?) - .map(|_| Decodable::decode(decoder)) - .collect::, _>>()?, - )) + fn decode(decoder: &mut D) -> &'tcx Self { + decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::>(), + ) } } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [thir::abstract_const::NodeId] { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - Ok(decoder.tcx().arena.alloc_from_iter( - (0..decoder.read_usize()?) - .map(|_| Decodable::decode(decoder)) - .collect::, _>>()?, - )) + fn decode(decoder: &mut D) -> &'tcx Self { + decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::>(), + ) } } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List { - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { - let len = decoder.read_usize()?; - decoder.tcx().mk_bound_variable_kinds((0..len).map(|_| Decodable::decode(decoder))) + fn decode(decoder: &mut D) -> &'tcx Self { + let len = decoder.read_usize(); + decoder.tcx().mk_bound_variable_kinds( + (0..len).map::(|_| Decodable::decode(decoder)), + ) } } @@ -391,6 +398,7 @@ impl_decodable_via_ref! { &'tcx ty::TypeckResults<'tcx>, &'tcx ty::List>, &'tcx ty::List>>, + &'tcx traits::ImplSource<'tcx, ()>, &'tcx Allocation, &'tcx mir::Body<'tcx>, &'tcx mir::UnsafetyCheckResult, @@ -405,7 +413,7 @@ macro_rules! __impl_decoder_methods { ($($name:ident -> $ty:ty;)*) => { $( #[inline] - fn $name(&mut self) -> Result<$ty, Self::Error> { + fn $name(&mut self) -> $ty { self.opaque.$name() } )* @@ -418,14 +426,14 @@ macro_rules! impl_arena_allocatable_decoder { [$name:ident: $ty:ty]) => { impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for $ty { #[inline] - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + fn decode(decoder: &mut D) -> &'tcx Self { decode_arena_allocable(decoder) } } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [$ty] { #[inline] - fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + fn decode(decoder: &mut D) -> &'tcx Self { decode_arena_allocable_slice(decoder) } } @@ -456,10 +464,8 @@ macro_rules! implement_ty_decoder { use super::$DecoderName; impl<$($typaram ),*> Decoder for $DecoderName<$($typaram),*> { - type Error = String; - $crate::__impl_decoder_methods! { - read_nil -> (); + read_unit -> (); read_u128 -> u128; read_u64 -> u64; @@ -483,13 +489,9 @@ macro_rules! implement_ty_decoder { } #[inline] - fn read_raw_bytes_into(&mut self, bytes: &mut [u8]) -> Result<(), Self::Error> { + fn read_raw_bytes_into(&mut self, bytes: &mut [u8]) { self.opaque.read_raw_bytes_into(bytes) } - - fn error(&mut self, err: &str) -> Self::Error { - self.opaque.error(err) - } } } } @@ -505,9 +507,9 @@ macro_rules! impl_binder_encode_decode { } } impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Binder<'tcx, $t> { - fn decode(decoder: &mut D) -> Result { - let bound_vars = Decodable::decode(decoder)?; - Ok(ty::Binder::bind_with_vars(Decodable::decode(decoder)?, bound_vars)) + fn decode(decoder: &mut D) -> Self { + let bound_vars = Decodable::decode(decoder); + ty::Binder::bind_with_vars(Decodable::decode(decoder), bound_vars) } } )* diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 27e22ccac0..a794a8c0e0 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -4,10 +4,12 @@ use crate::ty::{ self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, }; +use rustc_data_structures::intern::Interned; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::HashStable; +use std::fmt; mod int; mod kind; @@ -17,34 +19,53 @@ pub use int::*; pub use kind::*; pub use valtree::*; +/// Use this rather than `ConstS`, whenever possible. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] +#[cfg_attr(not(bootstrap), rustc_pass_by_value)] +pub struct Const<'tcx>(pub Interned<'tcx, ConstS<'tcx>>); + +impl<'tcx> fmt::Debug for Const<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This reflects what `Const` looked liked before `Interned` was + // introduced. We print it like this to avoid having to update expected + // output in a lot of tests. + write!(f, "Const {{ ty: {:?}, val: {:?} }}", self.ty(), self.val()) + } +} + /// Typed constant value. -#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] -#[derive(HashStable)] -pub struct Const<'tcx> { +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, HashStable, TyEncodable, TyDecodable)] +pub struct ConstS<'tcx> { pub ty: Ty<'tcx>, - pub val: ConstKind<'tcx>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(Const<'_>, 48); +static_assert_size!(ConstS<'_>, 48); impl<'tcx> Const<'tcx> { + pub fn ty(self) -> Ty<'tcx> { + self.0.ty + } + + pub fn val(self) -> ConstKind<'tcx> { + self.0.val + } + /// Literals and const generic parameters are eagerly converted to a constant, everything else /// becomes `Unevaluated`. - pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { + pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id)) } + #[instrument(skip(tcx), level = "debug")] pub fn from_opt_const_arg_anon_const( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, - ) -> &'tcx Self { + ) -> Self { debug!("Const::from_anon_const(def={:?})", def); - let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - - let body_id = match tcx.hir().get(hir_id) { + let body_id = match tcx.hir().get_by_def_id(def.did) { hir::Node::AnonConst(ac) => ac.body, _ => span_bug!( tcx.def_span(def.did.to_def_id()), @@ -53,15 +74,16 @@ impl<'tcx> Const<'tcx> { }; let expr = &tcx.hir().body(body_id).value; + debug!(?expr); let ty = tcx.type_of(def.def_id_for_type_of()); match Self::try_eval_lit_or_param(tcx, ty, expr) { Some(v) => v, - None => tcx.mk_const(ty::Const { + None => tcx.mk_const(ty::ConstS { val: ty::ConstKind::Unevaluated(ty::Unevaluated { def: def.to_global(), - substs_: None, + substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), promoted: None, }), ty, @@ -69,11 +91,21 @@ impl<'tcx> Const<'tcx> { } } + #[instrument(skip(tcx), level = "debug")] fn try_eval_lit_or_param( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, expr: &'tcx hir::Expr<'tcx>, - ) -> Option<&'tcx Self> { + ) -> Option { + // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments + // currently have to be wrapped in curly brackets, so it's necessary to special-case. + let expr = match &expr.kind { + hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { + block.expr.as_ref().unwrap() + } + _ => expr, + }; + let lit_input = match expr.kind { hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind { @@ -88,22 +120,17 @@ impl<'tcx> Const<'tcx> { if let Some(lit_input) = lit_input { // If an error occurred, ignore that it's a literal and leave reporting the error up to // mir. - if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { - return Some(c); - } else { - tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const"); + match tcx.at(expr.span).lit_to_const(lit_input) { + Ok(c) => return Some(c), + Err(e) => { + tcx.sess.delay_span_bug( + expr.span, + &format!("Const::from_anon_const: couldn't lit_to_const {:?}", e), + ); + } } } - // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments - // currently have to be wrapped in curly brackets, so it's necessary to special-case. - let expr = match &expr.kind { - hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { - block.expr.as_ref().unwrap() - } - _ => expr, - }; - use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; match expr.kind { ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { @@ -115,7 +142,7 @@ impl<'tcx> Const<'tcx> { let generics = tcx.generics_of(item_def_id.to_def_id()); let index = generics.param_def_id_to_index[&def_id]; let name = tcx.hir().name(hir_id); - Some(tcx.mk_const(ty::Const { + Some(tcx.mk_const(ty::ConstS { val: ty::ConstKind::Param(ty::ParamConst::new(index, name)), ty, })) @@ -124,7 +151,7 @@ impl<'tcx> Const<'tcx> { } } - pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { + pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { debug!("Const::from_inline_const(def_id={:?})", def_id); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); @@ -150,35 +177,35 @@ impl<'tcx> Const<'tcx> { let substs = InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty }) .substs; - tcx.mk_const(ty::Const { + tcx.mk_const(ty::ConstS { val: ty::ConstKind::Unevaluated(ty::Unevaluated { def: ty::WithOptConstParam::unknown(def_id).to_global(), - substs_: Some(substs), + substs, promoted: None, }), ty, }) } }; - debug_assert!(!ret.has_free_regions(tcx)); + debug_assert!(!ret.has_free_regions()); ret } /// Interns the given value as a constant. #[inline] - pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { - tcx.mk_const(Self { val: ConstKind::Value(val), ty }) + pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self { + tcx.mk_const(ConstS { val: ConstKind::Value(val), ty }) } #[inline] /// Interns the given scalar as a constant. - pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self { + pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> Self { Self::from_value(tcx, ConstValue::Scalar(val), ty) } #[inline] /// Creates a constant with the given integer value and interns it. - pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self { + pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> Self { let size = tcx .layout_of(ty) .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) @@ -188,19 +215,19 @@ impl<'tcx> Const<'tcx> { #[inline] /// Creates an interned zst constant. - pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { + pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { Self::from_scalar(tcx, Scalar::ZST, ty) } #[inline] /// Creates an interned bool constant. - pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self { + pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self { Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool)) } #[inline] /// Creates an interned usize constant. - pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self { + pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self { Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) } @@ -209,35 +236,35 @@ impl<'tcx> Const<'tcx> { /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it /// contains const generic parameters or pointers). pub fn try_eval_bits( - &self, + self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Option { - assert_eq!(self.ty, ty); + assert_eq!(self.ty(), ty); let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; // if `ty` does not depend on generic parameters, use an empty param_env - self.val.eval(tcx, param_env).try_to_bits(size) + self.val().eval(tcx, param_env).try_to_bits(size) } #[inline] - pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.val.eval(tcx, param_env).try_to_bool() + pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { + self.val().eval(tcx, param_env).try_to_bool() } #[inline] - pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.val.eval(tcx, param_env).try_to_machine_usize(tcx) + pub fn try_eval_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { + self.val().eval(tcx, param_env).try_to_machine_usize(tcx) } #[inline] /// 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>) -> &Const<'tcx> { - if let Some(val) = self.val.try_eval(tcx, param_env) { + pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> { + if let Some(val) = self.val().try_eval(tcx, param_env) { match val { - Ok(val) => Const::from_value(tcx, val, self.ty), - Err(ErrorReported) => tcx.const_error(self.ty), + Ok(val) => Const::from_value(tcx, val, self.ty()), + Err(ErrorReported) => tcx.const_error(self.ty()), } } else { self @@ -246,22 +273,21 @@ impl<'tcx> Const<'tcx> { #[inline] /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. - pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { + pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { self.try_eval_bits(tcx, param_env, ty) .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self)) } #[inline] /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. - pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { + pub fn eval_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { self.try_eval_usize(tcx, param_env) .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) } } -pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx Const<'tcx> { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let default_def_id = match tcx.hir().get(hir_id) { +pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Const<'tcx> { + let default_def_id = match tcx.hir().get_by_def_id(def_id.expect_local()) { hir::Node::GenericParam(hir::GenericParam { kind: hir::GenericParamKind::Const { ty: _, default: Some(ac) }, .. diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 1f4ebd0367..ca1db2fd55 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -147,8 +147,8 @@ impl Encodable for ScalarInt { } impl Decodable for ScalarInt { - fn decode(d: &mut D) -> Result { - Ok(ScalarInt { data: d.read_u128()?, size: d.read_u8()? }) + fn decode(d: &mut D) -> ScalarInt { + ScalarInt { data: d.read_u128(), size: d.read_u8() } } } @@ -294,12 +294,22 @@ impl From for ScalarInt { } } +/// Error returned when a conversion from ScalarInt to char fails. +#[derive(Debug)] +pub struct CharTryFromScalarInt; + impl TryFrom for char { - type Error = Size; + type Error = CharTryFromScalarInt; + #[inline] - fn try_from(int: ScalarInt) -> Result { - int.to_bits(Size::from_bytes(std::mem::size_of::())) - .map(|u| char::from_u32(u.try_into().unwrap()).unwrap()) + fn try_from(int: ScalarInt) -> Result { + let Ok(bits) = int.to_bits(Size::from_bytes(std::mem::size_of::())) else { + return Err(CharTryFromScalarInt); + }; + match char::from_u32(bits.try_into().unwrap()) { + Some(c) => Ok(c), + None => Err(CharTryFromScalarInt), + } } } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 7188eed544..af7c2c5cb4 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,5 +1,4 @@ use std::convert::TryInto; -use std::fmt; use crate::mir::interpret::{AllocId, ConstValue, Scalar}; use crate::mir::Promoted; @@ -13,17 +12,11 @@ use rustc_target::abi::Size; use super::ScalarInt; /// An unevaluated, potentially generic, constant. -/// -/// If `substs_` is `None` it means that this anon const -/// still has its default substs. -/// -/// We check for all possible substs in `fn default_anon_const_substs`, -/// so refer to that check for more info. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)] #[derive(Hash, HashStable)] pub struct Unevaluated<'tcx, P = Option> { pub def: ty::WithOptConstParam, - pub substs_: Option>, + pub substs: SubstsRef<'tcx>, pub promoted: P, } @@ -31,34 +24,21 @@ impl<'tcx> Unevaluated<'tcx> { #[inline] pub fn shrink(self) -> Unevaluated<'tcx, ()> { debug_assert_eq!(self.promoted, None); - Unevaluated { def: self.def, substs_: self.substs_, promoted: () } + Unevaluated { def: self.def, substs: self.substs, promoted: () } } } impl<'tcx> Unevaluated<'tcx, ()> { #[inline] pub fn expand(self) -> Unevaluated<'tcx> { - Unevaluated { def: self.def, substs_: self.substs_, promoted: None } + Unevaluated { def: self.def, substs: self.substs, promoted: None } } } impl<'tcx, P: Default> Unevaluated<'tcx, P> { #[inline] pub fn new(def: ty::WithOptConstParam, substs: SubstsRef<'tcx>) -> Unevaluated<'tcx, P> { - Unevaluated { def, substs_: Some(substs), promoted: Default::default() } - } -} - -impl<'tcx, P: Default + PartialEq + fmt::Debug> Unevaluated<'tcx, P> { - #[inline] - pub fn substs(self, tcx: TyCtxt<'tcx>) -> SubstsRef<'tcx> { - self.substs_.unwrap_or_else(|| { - // We must not use the parents default substs for promoted constants - // as that can result in incorrect substs and calls the `default_anon_const_substs` - // for something that might not actually be a constant. - debug_assert_eq!(self.promoted, Default::default()); - tcx.default_anon_const_substs(self.def.did) - }) + Unevaluated { def, substs, promoted: Default::default() } } } @@ -173,7 +153,7 @@ impl<'tcx> ConstKind<'tcx> { let param_env_and = if param_env_and.needs_infer() { tcx.param_env(unevaluated.def.did).and(ty::Unevaluated { def: unevaluated.def, - substs_: Some(InternalSubsts::identity_for_item(tcx, unevaluated.def.did)), + substs: InternalSubsts::identity_for_item(tcx, unevaluated.def.did), promoted: unevaluated.promoted, }) } else { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index dd571e29bf..41145d2501 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -5,10 +5,12 @@ use crate::dep_graph::{DepGraph, DepKind, DepKindStruct}; use crate::hir::place::Place as HirPlace; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; -use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath, ObjectLifetimeDefault}; +use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath}; use crate::middle::stability; use crate::mir::interpret::{self, Allocation, ConstValue, Scalar}; -use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted}; +use crate::mir::{ + Body, BorrowCheckResult, Field, Local, Place, PlaceElem, ProjectionKind, Promoted, +}; use crate::thir::Thir; use crate::traits; use crate::ty::query::{self, TyCtxtAt}; @@ -16,14 +18,15 @@ use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, Substs use crate::ty::TyKind::*; use crate::ty::{ self, AdtDef, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig, - ClosureSizeProfileData, 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, + ClosureSizeProfileData, Const, ConstS, ConstVid, DefIdTree, ExistentialPredicate, FloatTy, + FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy, IntTy, IntVar, IntVid, List, + ParamConst, ParamTy, PolyFnSig, Predicate, PredicateKind, PredicateS, ProjectionTy, Region, + RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut, UintTy, }; use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::intern::Interned; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; @@ -89,7 +92,7 @@ pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync { #[derive(TyEncodable, TyDecodable, HashStable)] pub struct DelaySpanBugEmitted(()); -type InternedSet<'tcx, T> = ShardedHashMap, ()>; +type InternedSet<'tcx, T> = ShardedHashMap, ()>; pub struct CtxtInterners<'tcx> { /// The arena that types, regions, etc. are allocated from. @@ -104,15 +107,21 @@ pub struct CtxtInterners<'tcx> { region: InternedSet<'tcx, RegionKind>, poly_existential_predicates: InternedSet<'tcx, List>>>, - predicate: InternedSet<'tcx, PredicateInner<'tcx>>, + predicate: InternedSet<'tcx, PredicateS<'tcx>>, predicates: InternedSet<'tcx, List>>, projs: InternedSet<'tcx, List>, place_elems: InternedSet<'tcx, List>>, - const_: InternedSet<'tcx, Const<'tcx>>, + const_: InternedSet<'tcx, ConstS<'tcx>>, const_allocation: InternedSet<'tcx, Allocation>, bound_variable_kinds: InternedSet<'tcx, List>, layout: InternedSet<'tcx, Layout>, adt_def: InternedSet<'tcx, AdtDef>, + + /// `#[stable]` and `#[unstable]` attributes + stability: InternedSet<'tcx, attr::Stability>, + + /// `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes + const_stability: InternedSet<'tcx, attr::ConstStability>, } impl<'tcx> CtxtInterners<'tcx> { @@ -134,6 +143,8 @@ impl<'tcx> CtxtInterners<'tcx> { bound_variable_kinds: Default::default(), layout: Default::default(), adt_def: Default::default(), + stability: Default::default(), + const_stability: Default::default(), } } @@ -141,39 +152,40 @@ impl<'tcx> CtxtInterners<'tcx> { #[allow(rustc::usage_of_ty_tykind)] #[inline(never)] fn intern_ty(&self, kind: TyKind<'tcx>) -> Ty<'tcx> { - self.type_ - .intern(kind, |kind| { - let flags = super::flags::FlagComputation::for_kind(&kind); - - let ty_struct = TyS { - kind, - flags: flags.flags, - outer_exclusive_binder: flags.outer_exclusive_binder, - }; + Ty(Interned::new_unchecked( + self.type_ + .intern(kind, |kind| { + let flags = super::flags::FlagComputation::for_kind(&kind); + + let ty_struct = TyS { + kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; - Interned(self.arena.alloc(ty_struct)) - }) - .0 + InternedInSet(self.arena.alloc(ty_struct)) + }) + .0, + )) } #[inline(never)] - fn intern_predicate( - &self, - kind: Binder<'tcx, PredicateKind<'tcx>>, - ) -> &'tcx PredicateInner<'tcx> { - self.predicate - .intern(kind, |kind| { - let flags = super::flags::FlagComputation::for_predicate(kind); - - let predicate_struct = PredicateInner { - kind, - flags: flags.flags, - outer_exclusive_binder: flags.outer_exclusive_binder, - }; + fn intern_predicate(&self, kind: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> { + Predicate(Interned::new_unchecked( + self.predicate + .intern(kind, |kind| { + let flags = super::flags::FlagComputation::for_predicate(kind); + + let predicate_struct = PredicateS { + kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; - Interned(self.arena.alloc(predicate_struct)) - }) - .0 + InternedInSet(self.arena.alloc(predicate_struct)) + }) + .0, + )) } } @@ -212,12 +224,12 @@ pub struct CommonLifetimes<'tcx> { /// `ReStatic` pub re_static: Region<'tcx>, - /// Erased region, used after type-checking + /// Erased region, used outside of type inference. pub re_erased: Region<'tcx>, } pub struct CommonConsts<'tcx> { - pub unit: &'tcx Const<'tcx>, + pub unit: Const<'tcx>, } pub struct LocalTableInContext<'a, V> { @@ -352,7 +364,7 @@ pub struct TypeckResults<'tcx> { field_indices: ItemLocalMap, /// Stores the types for various nodes in the AST. Note that this table - /// is not guaranteed to be populated until after typeck. See + /// is not guaranteed to be populated outside inference. See /// typeck::check::fn_ctxt for details. node_types: ItemLocalMap>, @@ -848,16 +860,16 @@ impl<'tcx> CanonicalUserType<'tcx> { _ => false, }, - GenericArgKind::Lifetime(r) => match r { + GenericArgKind::Lifetime(r) => match *r { ty::ReLateBound(debruijn, br) => { // We only allow a `ty::INNERMOST` index in substitutions. - assert_eq!(*debruijn, ty::INNERMOST); + assert_eq!(debruijn, ty::INNERMOST); cvar == br.var } _ => false, }, - GenericArgKind::Const(ct) => match ct.val { + GenericArgKind::Const(ct) => match ct.val() { ty::ConstKind::Bound(debruijn, b) => { // We only allow a `ty::INNERMOST` index in substitutions. assert_eq!(debruijn, ty::INNERMOST); @@ -918,22 +930,30 @@ impl<'tcx> CommonTypes<'tcx> { impl<'tcx> CommonLifetimes<'tcx> { fn new(interners: &CtxtInterners<'tcx>) -> CommonLifetimes<'tcx> { - let mk = |r| interners.region.intern(r, |r| Interned(interners.arena.alloc(r))).0; + let mk = |r| { + Region(Interned::new_unchecked( + interners.region.intern(r, |r| InternedInSet(interners.arena.alloc(r))).0, + )) + }; CommonLifetimes { - re_root_empty: mk(RegionKind::ReEmpty(ty::UniverseIndex::ROOT)), - re_static: mk(RegionKind::ReStatic), - re_erased: mk(RegionKind::ReErased), + re_root_empty: mk(ty::ReEmpty(ty::UniverseIndex::ROOT)), + re_static: mk(ty::ReStatic), + re_erased: mk(ty::ReErased), } } } impl<'tcx> CommonConsts<'tcx> { fn new(interners: &CtxtInterners<'tcx>, types: &CommonTypes<'tcx>) -> CommonConsts<'tcx> { - let mk_const = |c| interners.const_.intern(c, |c| Interned(interners.arena.alloc(c))).0; + let mk_const = |c| { + Const(Interned::new_unchecked( + interners.const_.intern(c, |c| InternedInSet(interners.arena.alloc(c))).0, + )) + }; CommonConsts { - unit: mk_const(ty::Const { + unit: mk_const(ty::ConstS { val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::ZST)), ty: types.unit, }), @@ -961,6 +981,7 @@ pub struct FreeRegionInfo { /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ty.html #[derive(Copy, Clone)] #[rustc_diagnostic_item = "TyCtxt"] +#[cfg_attr(not(bootstrap), rustc_pass_by_value)] pub struct TyCtxt<'tcx> { gcx: &'tcx GlobalCtxt<'tcx>, } @@ -1034,12 +1055,6 @@ pub struct GlobalCtxt<'tcx> { /// Data layout specification for the current target. pub data_layout: TargetDataLayout, - /// `#[stable]` and `#[unstable]` attributes - stability_interner: ShardedHashMap<&'tcx attr::Stability, ()>, - - /// `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes - const_stability_interner: ShardedHashMap<&'tcx attr::ConstStability, ()>, - /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock>, @@ -1058,6 +1073,17 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn mir_borrowck_opt_const_arg( + self, + def: ty::WithOptConstParam, + ) -> &'tcx BorrowCheckResult<'tcx> { + if let Some(param_did) = def.const_param_did { + self.mir_borrowck_const_arg((def.did, param_did)) + } else { + self.mir_borrowck(def.did) + } + } + pub fn alloc_steal_thir(self, thir: Thir<'tcx>) -> &'tcx Steal> { self.arena.alloc(Steal::new(thir)) } @@ -1091,16 +1117,6 @@ impl<'tcx> TyCtxt<'tcx> { self.create_memory_alloc(alloc) } - // FIXME(eddyb) move to `direct_interners!`. - pub fn intern_stability(self, stab: attr::Stability) -> &'tcx attr::Stability { - self.stability_interner.intern(stab, |stab| self.arena.alloc(stab)) - } - - // FIXME(eddyb) move to `direct_interners!`. - pub fn intern_const_stability(self, stab: attr::ConstStability) -> &'tcx attr::ConstStability { - self.const_stability_interner.intern(stab, |stab| self.arena.alloc(stab)) - } - /// Returns a range of the start/end indices specified with the /// `rustc_layout_scalar_valid_range` attribute. // FIXME(eddyb) this is an awkward spot for this method, maybe move it? @@ -1184,8 +1200,6 @@ impl<'tcx> TyCtxt<'tcx> { evaluation_cache: Default::default(), crate_name: Symbol::intern(crate_name), data_layout, - stability_interner: Default::default(), - const_stability_interner: Default::default(), alloc_map: Lock::new(interpret::AllocMap::new()), output_filenames: Arc::new(output_filenames), } @@ -1209,12 +1223,26 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_ty(Error(DelaySpanBugEmitted(()))) } - /// Like `err` but for constants. + /// Like [TyCtxt::ty_error] but for constants. #[track_caller] - pub fn const_error(self, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { - self.sess - .delay_span_bug(DUMMY_SP, "ty::ConstKind::Error constructed but no error reported."); - self.mk_const(ty::Const { val: ty::ConstKind::Error(DelaySpanBugEmitted(())), ty }) + pub fn const_error(self, ty: Ty<'tcx>) -> Const<'tcx> { + self.const_error_with_message( + ty, + DUMMY_SP, + "ty::ConstKind::Error constructed but no error reported", + ) + } + + /// Like [TyCtxt::ty_error_with_message] but for constants. + #[track_caller] + pub fn const_error_with_message>( + self, + ty: Ty<'tcx>, + span: S, + msg: &str, + ) -> Const<'tcx> { + self.sess.delay_span_bug(span, msg); + self.mk_const(ty::ConstS { val: ty::ConstKind::Error(DelaySpanBugEmitted(())), ty }) } pub fn consider_optimizing String>(self, msg: T) -> bool { @@ -1307,7 +1335,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation /// session, if it still exists. This is used during incremental compilation to /// turn a deserialized `DefPathHash` into its current `DefId`. - pub fn def_path_hash_to_def_id(self, hash: DefPathHash) -> DefId { + pub fn def_path_hash_to_def_id(self, hash: DefPathHash, err: &mut dyn FnMut() -> !) -> DefId { debug!("def_path_hash_to_def_id({:?})", hash); let stable_crate_id = hash.stable_crate_id(); @@ -1315,7 +1343,10 @@ impl<'tcx> TyCtxt<'tcx> { // If this is a DefPathHash from the local crate, we can look up the // DefId in the tcx's `Definitions`. if stable_crate_id == self.sess.local_stable_crate_id() { - self.untracked_resolutions.definitions.local_def_path_hash_to_def_id(hash).to_def_id() + self.untracked_resolutions + .definitions + .local_def_path_hash_to_def_id(hash, err) + .to_def_id() } else { // If this is a DefPathHash from an upstream crate, let the CrateStore map // it to a DefId. @@ -1461,8 +1492,7 @@ impl<'tcx> TyCtxt<'tcx> { _ => return None, // not a free region }; - let hir_id = self.hir().local_def_id_to_hir_id(suitable_region_binding_scope); - let is_impl_item = match self.hir().find(hir_id) { + let is_impl_item = match self.hir().find_by_def_id(suitable_region_binding_scope) { Some(Node::Item(..) | Node::TraitItem(..)) => false, Some(Node::ImplItem(..)) => { self.is_bound_region_in_impl_item(suitable_region_binding_scope) @@ -1495,8 +1525,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> { // `type_of()` will fail on these (#55796, #86483), so only allow `fn`s or closures. - let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id); - match self.hir().get(hir_id) { + match self.hir().get_by_def_id(scope_def_id) { Node::Item(&hir::Item { kind: ItemKind::Fn(..), .. }) => {} Node::TraitItem(&hir::TraitItem { kind: TraitItemKind::Fn(..), .. }) => {} Node::ImplItem(&hir::ImplItem { kind: ImplItemKind::Fn(..), .. }) => {} @@ -1510,6 +1539,7 @@ impl<'tcx> TyCtxt<'tcx> { let sig = ret_ty.fn_sig(self); let output = self.erase_late_bound_regions(sig.output()); if output.is_impl_trait() { + let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id); let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap(); Some((output, fn_decl.output.span())) } else { @@ -1607,12 +1637,28 @@ pub trait Lift<'tcx>: fmt::Debug { fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option; } +// Deprecated: we are in the process of converting all uses to `nop_lift`. +macro_rules! nop_lift_old { + ($set:ident; $ty:ty => $lifted:ty) => { + impl<'a, 'tcx> Lift<'tcx> for $ty { + type Lifted = $lifted; + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + if tcx.interners.$set.contains_pointer_to(&InternedInSet(self)) { + Some(unsafe { mem::transmute(self) }) + } else { + None + } + } + } + }; +} + macro_rules! nop_lift { ($set:ident; $ty:ty => $lifted:ty) => { impl<'a, 'tcx> Lift<'tcx> for $ty { type Lifted = $lifted; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - if tcx.interners.$set.contains_pointer_to(&Interned(self)) { + if tcx.interners.$set.contains_pointer_to(&InternedInSet(self.0.0)) { Some(unsafe { mem::transmute(self) }) } else { None @@ -1630,7 +1676,7 @@ macro_rules! nop_list_lift { if self.is_empty() { return Some(List::empty()); } - if tcx.interners.$set.contains_pointer_to(&Interned(self)) { + if tcx.interners.$set.contains_pointer_to(&InternedInSet(self)) { Some(unsafe { mem::transmute(self) }) } else { None @@ -1642,9 +1688,9 @@ macro_rules! nop_list_lift { nop_lift! {type_; Ty<'a> => Ty<'tcx>} nop_lift! {region; Region<'a> => Region<'tcx>} -nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>} -nop_lift! {const_allocation; &'a Allocation => &'tcx Allocation} -nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>} +nop_lift! {const_; Const<'a> => Const<'tcx>} +nop_lift_old! {const_allocation; &'a Allocation => &'tcx Allocation} +nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>} nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>} nop_list_lift! {poly_existential_predicates; ty::Binder<'a, ExistentialPredicate<'a>> => ty::Binder<'tcx, ExistentialPredicate<'tcx>>} @@ -1661,7 +1707,7 @@ CloneLiftImpls! { for<'tcx> { Constness, traits::WellFormedLoc, } } pub mod tls { use super::{ptr_eq, GlobalCtxt, TyCtxt}; - use crate::dep_graph::{DepKind, TaskDeps}; + use crate::dep_graph::TaskDepsRef; use crate::ty::query; use rustc_data_structures::sync::{self, Lock}; use rustc_data_structures::thin_vec::ThinVec; @@ -1686,7 +1732,7 @@ pub mod tls { /// The current query job, if any. This is updated by `JobOwner::start` in /// `ty::query::plumbing` when executing a query. - pub query: Option>, + pub query: Option, /// Where to store diagnostics for the current query job, if any. /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query. @@ -1697,13 +1743,19 @@ pub mod tls { /// The current dep graph task. This is used to add dependencies to queries /// when executing them. - pub task_deps: Option<&'a Lock>, + pub task_deps: TaskDepsRef<'a>, } impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self { let tcx = TyCtxt { gcx }; - ImplicitCtxt { tcx, query: None, diagnostics: None, layout_depth: 0, task_deps: None } + ImplicitCtxt { + tcx, + query: None, + diagnostics: None, + layout_depth: 0, + task_deps: TaskDepsRef::Ignore, + } } } @@ -1831,7 +1883,7 @@ macro_rules! sty_debug_print { #[allow(non_snake_case)] mod inner { use crate::ty::{self, TyCtxt}; - use crate::ty::context::Interned; + use crate::ty::context::InternedInSet; #[derive(Copy, Clone)] struct DebugStat { @@ -1854,16 +1906,16 @@ macro_rules! sty_debug_print { let shards = tcx.interners.type_.lock_shards(); let types = shards.iter().flat_map(|shard| shard.keys()); - for &Interned(t) in types { - let variant = match t.kind() { + for &InternedInSet(t) in types { + let variant = match t.kind { ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::Never => continue, ty::Error(_) => /* unimportant */ continue, $(ty::$variant(..) => &mut $variant,)* }; - let lt = t.flags().intersects(ty::TypeFlags::HAS_RE_INFER); - let ty = t.flags().intersects(ty::TypeFlags::HAS_TY_INFER); - let ct = t.flags().intersects(ty::TypeFlags::HAS_CT_INFER); + let lt = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER); + let ty = t.flags.intersects(ty::TypeFlags::HAS_TY_INFER); + let ct = t.flags.intersects(ty::TypeFlags::HAS_CT_INFER); variant.total += 1; total.total += 1; @@ -1929,11 +1981,11 @@ impl<'tcx> TyCtxt<'tcx> { writeln!(fmt, "InternalSubsts interner: #{}", self.0.interners.substs.len())?; writeln!(fmt, "Region interner: #{}", self.0.interners.region.len())?; - writeln!(fmt, "Stability interner: #{}", self.0.stability_interner.len())?; + writeln!(fmt, "Stability interner: #{}", self.0.interners.stability.len())?; writeln!( fmt, "Const Stability interner: #{}", - self.0.const_stability_interner.len() + self.0.interners.const_stability.len() )?; writeln!( fmt, @@ -1950,122 +2002,180 @@ impl<'tcx> TyCtxt<'tcx> { } } -/// An entry in an interner. -struct Interned<'tcx, T: ?Sized>(&'tcx T); +// This type holds a `T` in the interner. The `T` is stored in the arena and +// this type just holds a pointer to it, but it still effectively owns it. It +// impls `Borrow` so that it can be looked up using the original +// (non-arena-memory-owning) types. +struct InternedInSet<'tcx, T: ?Sized>(&'tcx T); -impl<'tcx, T: 'tcx + ?Sized> Clone for Interned<'tcx, T> { +impl<'tcx, T: 'tcx + ?Sized> Clone for InternedInSet<'tcx, T> { fn clone(&self) -> Self { - Interned(self.0) + InternedInSet(self.0) } } -impl<'tcx, T: 'tcx + ?Sized> Copy for Interned<'tcx, T> {} -impl<'tcx, T: 'tcx + ?Sized> IntoPointer for Interned<'tcx, T> { +impl<'tcx, T: 'tcx + ?Sized> Copy for InternedInSet<'tcx, T> {} + +impl<'tcx, T: 'tcx + ?Sized> IntoPointer for InternedInSet<'tcx, T> { fn into_pointer(&self) -> *const () { self.0 as *const _ as *const () } } -// N.B., an `Interned` compares and hashes as a `TyKind`. -impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> { - fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool { - self.0.kind() == other.0.kind() - } -} - -impl<'tcx> Eq for Interned<'tcx, TyS<'tcx>> {} - -impl<'tcx> Hash for Interned<'tcx, TyS<'tcx>> { - fn hash(&self, s: &mut H) { - self.0.kind().hash(s) - } -} #[allow(rustc::usage_of_ty_tykind)] -impl<'tcx> Borrow> for Interned<'tcx, TyS<'tcx>> { +impl<'tcx> Borrow> for InternedInSet<'tcx, TyS<'tcx>> { fn borrow<'a>(&'a self) -> &'a TyKind<'tcx> { - &self.0.kind() + &self.0.kind } } -// N.B., an `Interned` compares and hashes as a `PredicateKind`. -impl<'tcx> PartialEq for Interned<'tcx, PredicateInner<'tcx>> { - fn eq(&self, other: &Interned<'tcx, PredicateInner<'tcx>>) -> bool { + +impl<'tcx> PartialEq for InternedInSet<'tcx, TyS<'tcx>> { + fn eq(&self, other: &InternedInSet<'tcx, TyS<'tcx>>) -> bool { + // The `Borrow` trait requires that `x.borrow() == y.borrow()` equals + // `x == y`. self.0.kind == other.0.kind } } -impl<'tcx> Eq for Interned<'tcx, PredicateInner<'tcx>> {} +impl<'tcx> Eq for InternedInSet<'tcx, TyS<'tcx>> {} -impl<'tcx> Hash for Interned<'tcx, PredicateInner<'tcx>> { +impl<'tcx> Hash for InternedInSet<'tcx, TyS<'tcx>> { fn hash(&self, s: &mut H) { + // The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`. self.0.kind.hash(s) } } -impl<'tcx> Borrow>> for Interned<'tcx, PredicateInner<'tcx>> { +impl<'tcx> Borrow>> for InternedInSet<'tcx, PredicateS<'tcx>> { fn borrow<'a>(&'a self) -> &'a Binder<'tcx, PredicateKind<'tcx>> { &self.0.kind } } -// N.B., an `Interned>` compares and hashes as its elements. -impl<'tcx, T: PartialEq> PartialEq for Interned<'tcx, List> { - fn eq(&self, other: &Interned<'tcx, List>) -> bool { - self.0[..] == other.0[..] +impl<'tcx> PartialEq for InternedInSet<'tcx, PredicateS<'tcx>> { + fn eq(&self, other: &InternedInSet<'tcx, PredicateS<'tcx>>) -> bool { + // The `Borrow` trait requires that `x.borrow() == y.borrow()` equals + // `x == y`. + self.0.kind == other.0.kind } } -impl<'tcx, T: Eq> Eq for Interned<'tcx, List> {} +impl<'tcx> Eq for InternedInSet<'tcx, PredicateS<'tcx>> {} -impl<'tcx, T: Hash> Hash for Interned<'tcx, List> { +impl<'tcx> Hash for InternedInSet<'tcx, PredicateS<'tcx>> { fn hash(&self, s: &mut H) { - self.0[..].hash(s) + // The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`. + self.0.kind.hash(s) } } -impl<'tcx, T> Borrow<[T]> for Interned<'tcx, List> { +impl<'tcx, T> Borrow<[T]> for InternedInSet<'tcx, List> { fn borrow<'a>(&'a self) -> &'a [T] { &self.0[..] } } +impl<'tcx, T: PartialEq> PartialEq for InternedInSet<'tcx, List> { + fn eq(&self, other: &InternedInSet<'tcx, List>) -> bool { + // The `Borrow` trait requires that `x.borrow() == y.borrow()` equals + // `x == y`. + self.0[..] == other.0[..] + } +} + +impl<'tcx, T: Eq> Eq for InternedInSet<'tcx, List> {} + +impl<'tcx, T: Hash> Hash for InternedInSet<'tcx, List> { + fn hash(&self, s: &mut H) { + // The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`. + self.0[..].hash(s) + } +} + macro_rules! direct_interners { - ($($name:ident: $method:ident($ty:ty),)+) => { - $(impl<'tcx> PartialEq for Interned<'tcx, $ty> { + ($($name:ident: $method:ident($ty:ty): $ret_ctor:ident -> $ret_ty:ty,)+) => { + $(impl<'tcx> Borrow<$ty> for InternedInSet<'tcx, $ty> { + fn borrow<'a>(&'a self) -> &'a $ty { + &self.0 + } + } + + impl<'tcx> PartialEq for InternedInSet<'tcx, $ty> { fn eq(&self, other: &Self) -> bool { + // The `Borrow` trait requires that `x.borrow() == y.borrow()` + // equals `x == y`. self.0 == other.0 } } - impl<'tcx> Eq for Interned<'tcx, $ty> {} + impl<'tcx> Eq for InternedInSet<'tcx, $ty> {} - impl<'tcx> Hash for Interned<'tcx, $ty> { + impl<'tcx> Hash for InternedInSet<'tcx, $ty> { fn hash(&self, s: &mut H) { + // The `Borrow` trait requires that `x.borrow().hash(s) == + // x.hash(s)`. self.0.hash(s) } } - impl<'tcx> Borrow<$ty> for Interned<'tcx, $ty> { + impl<'tcx> TyCtxt<'tcx> { + pub fn $method(self, v: $ty) -> $ret_ty { + $ret_ctor(Interned::new_unchecked(self.interners.$name.intern(v, |v| { + InternedInSet(self.interners.arena.alloc(v)) + }).0)) + } + })+ + } +} + +direct_interners! { + region: mk_region(RegionKind): Region -> Region<'tcx>, + const_: mk_const(ConstS<'tcx>): Const -> Const<'tcx>, +} + +macro_rules! direct_interners_old { + ($($name:ident: $method:ident($ty:ty),)+) => { + $(impl<'tcx> Borrow<$ty> for InternedInSet<'tcx, $ty> { fn borrow<'a>(&'a self) -> &'a $ty { &self.0 } } + impl<'tcx> PartialEq for InternedInSet<'tcx, $ty> { + fn eq(&self, other: &Self) -> bool { + // The `Borrow` trait requires that `x.borrow() == y.borrow()` + // equals `x == y`. + self.0 == other.0 + } + } + + impl<'tcx> Eq for InternedInSet<'tcx, $ty> {} + + impl<'tcx> Hash for InternedInSet<'tcx, $ty> { + fn hash(&self, s: &mut H) { + // The `Borrow` trait requires that `x.borrow().hash(s) == + // x.hash(s)`. + self.0.hash(s) + } + } + impl<'tcx> TyCtxt<'tcx> { pub fn $method(self, v: $ty) -> &'tcx $ty { self.interners.$name.intern(v, |v| { - Interned(self.interners.arena.alloc(v)) + InternedInSet(self.interners.arena.alloc(v)) }).0 } })+ } } -direct_interners! { - region: mk_region(RegionKind), - const_: mk_const(Const<'tcx>), +// FIXME: eventually these should all be converted to `direct_interners`. +direct_interners_old! { const_allocation: intern_const_alloc(Allocation), layout: intern_layout(Layout), adt_def: intern_adt_def(AdtDef), + stability: intern_stability(attr::Stability), + const_stability: intern_const_stability(attr::ConstStability), } macro_rules! slice_interners { @@ -2073,7 +2183,7 @@ macro_rules! slice_interners { impl<'tcx> TyCtxt<'tcx> { $(pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> { self.interners.$field.intern_ref(v, || { - Interned(List::from_arena(&*self.arena, v)) + InternedInSet(List::from_arena(&*self.arena, v)) }).0 })+ } @@ -2173,8 +2283,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn mk_predicate(self, binder: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> { - let inner = self.interners.intern_predicate(binder); - Predicate { inner } + self.interners.intern_predicate(binder) } #[inline] @@ -2385,8 +2494,8 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_const_var(self, v: ConstVid<'tcx>, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { - self.mk_const(ty::Const { val: ty::ConstKind::Infer(InferConst::Var(v)), ty }) + pub fn mk_const_var(self, v: ConstVid<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { + self.mk_const(ty::ConstS { val: ty::ConstKind::Infer(InferConst::Var(v)), ty }) } #[inline] @@ -2405,8 +2514,8 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_const_infer(self, ic: InferConst<'tcx>, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> { - self.mk_const(ty::Const { val: ty::ConstKind::Infer(ic), ty }) + pub fn mk_const_infer(self, ic: InferConst<'tcx>, ty: Ty<'tcx>) -> ty::Const<'tcx> { + self.mk_const(ty::ConstS { val: ty::ConstKind::Infer(ic), ty }) } #[inline] @@ -2415,8 +2524,8 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_const_param(self, index: u32, name: Symbol, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { - self.mk_const(ty::Const { val: ty::ConstKind::Param(ParamConst { index, name }), ty }) + pub fn mk_const_param(self, index: u32, name: Symbol, ty: Ty<'tcx>) -> Const<'tcx> { + self.mk_const(ty::ConstS { val: ty::ConstKind::Param(ParamConst { index, name }), ty }) } pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> { @@ -2452,7 +2561,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> Place<'tcx> { self.mk_place_elem( place, - PlaceElem::Downcast(Some(adt_def.variants[variant_index].ident.name), variant_index), + PlaceElem::Downcast(Some(adt_def.variants[variant_index].name), variant_index), ) } @@ -2677,10 +2786,6 @@ impl<'tcx> TyCtxt<'tcx> { .map_or(false, |(owner, set)| owner == id.owner && set.contains(&id.local_id)) } - pub fn object_lifetime_defaults(self, id: HirId) -> Option> { - self.object_lifetime_defaults_map(id.owner) - } - pub fn late_bound_vars(self, id: HirId) -> &'tcx List { self.mk_bound_variable_kinds( self.late_bound_vars_map(id.owner) @@ -2692,8 +2797,8 @@ impl<'tcx> TyCtxt<'tcx> { ) } - pub fn lifetime_scope(self, id: HirId) -> Option { - self.lifetime_scope_map(id.owner).and_then(|mut map| map.remove(&id.local_id)) + pub fn lifetime_scope(self, id: HirId) -> Option<&'tcx LifetimeScopeForPath> { + self.lifetime_scope_map(id.owner).as_ref().and_then(|map| map.get(&id.local_id)) } /// Whether the `def_id` counts as const fn in the current crate, considering all active @@ -2763,8 +2868,33 @@ pub trait InternIteratorElement: Sized { impl InternIteratorElement for T { type Output = R; - fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { - f(&iter.collect::>()) + fn intern_with, F: FnOnce(&[T]) -> R>( + mut iter: I, + f: F, + ) -> Self::Output { + // This code is hot enough that it's worth specializing for the most + // common length lists, to avoid the overhead of `SmallVec` creation. + // Lengths 0, 1, and 2 typically account for ~95% of cases. If + // `size_hint` is incorrect a panic will occur via an `unwrap` or an + // `assert`. + match iter.size_hint() { + (0, Some(0)) => { + assert!(iter.next().is_none()); + f(&[]) + } + (1, Some(1)) => { + let t0 = iter.next().unwrap(); + assert!(iter.next().is_none()); + f(&[t0]) + } + (2, Some(2)) => { + let t0 = iter.next().unwrap(); + let t1 = iter.next().unwrap(); + assert!(iter.next().is_none()); + f(&[t0, t1]) + } + _ => f(&iter.collect::>()), + } } } @@ -2774,6 +2904,7 @@ where { type Output = R; fn intern_with, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { + // This code isn't hot. f(&iter.cloned().collect::>()) } } @@ -2786,10 +2917,15 @@ impl InternIteratorElement for Result { ) -> Self::Output { // This code is hot enough that it's worth specializing for the most // common length lists, to avoid the overhead of `SmallVec` creation. - // The match arms are in order of frequency. The 1, 2, and 0 cases are - // typically hit in ~95% of cases. We assume that if the upper and - // lower bounds from `size_hint` agree they are correct. + // Lengths 0, 1, and 2 typically account for ~95% of cases. If + // `size_hint` is incorrect a panic will occur via an `unwrap` or an + // `assert`, unless a failure happens first, in which case the result + // will be an error anyway. Ok(match iter.size_hint() { + (0, Some(0)) => { + assert!(iter.next().is_none()); + f(&[]) + } (1, Some(1)) => { let t0 = iter.next().unwrap()?; assert!(iter.next().is_none()); @@ -2801,10 +2937,6 @@ impl InternIteratorElement for Result { assert!(iter.next().is_none()); f(&[t0, t1]) } - (0, Some(0)) => { - assert!(iter.next().is_none()); - f(&[]) - } _ => f(&iter.collect::, _>>()?), }) } @@ -2817,10 +2949,9 @@ fn ptr_eq(t: *const T, u: *const U) -> bool { } pub fn provide(providers: &mut ty::query::Providers) { - providers.in_scope_traits_map = - |tcx, id| tcx.hir_crate(()).owners[id].as_ref().map(|owner_info| &owner_info.trait_map); providers.resolutions = |tcx, ()| &tcx.untracked_resolutions; - providers.module_exports = |tcx, id| tcx.resolutions(()).export_map.get(&id).map(|v| &v[..]); + providers.module_reexports = + |tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]); providers.crate_name = |tcx, id| { assert_eq!(id, LOCAL_CRATE); tcx.crate_name @@ -2840,7 +2971,7 @@ pub fn provide(providers: &mut ty::query::Providers) { |tcx, id| tcx.stability().local_deprecation_entry(id.expect_local()); providers.extern_mod_stmt_cnum = |tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned(); - providers.output_filenames = |tcx, ()| tcx.output_filenames.clone(); + providers.output_filenames = |tcx, ()| &tcx.output_filenames; providers.features_query = |tcx, ()| tcx.sess.features_untracked(); providers.is_panic_runtime = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index ee00f6c62f..64b2edd2c3 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -1,10 +1,10 @@ -//! Diagnostics related methods for `TyS`. +//! Diagnostics related methods for `Ty`. use crate::ty::subst::{GenericArg, GenericArgKind}; use crate::ty::TyKind::*; use crate::ty::{ ConstKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, InferTy, - ProjectionTy, TyCtxt, TyS, TypeAndMut, + ProjectionTy, Term, Ty, TyCtxt, TypeAndMut, }; use rustc_errors::{Applicability, DiagnosticBuilder}; @@ -13,9 +13,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; use rustc_span::Span; -impl<'tcx> TyS<'tcx> { - /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. - pub fn is_primitive_ty(&self) -> bool { +impl<'tcx> Ty<'tcx> { + /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive. + pub fn is_primitive_ty(self) -> bool { matches!( self.kind(), Bool | Char @@ -34,7 +34,7 @@ impl<'tcx> TyS<'tcx> { /// Whether the type is succinctly representable as a type instead of just referred to with a /// description in error messages. This is used in the main error message. - pub fn is_simple_ty(&self) -> bool { + pub fn is_simple_ty(self) -> bool { match self.kind() { Bool | Char @@ -58,7 +58,7 @@ impl<'tcx> TyS<'tcx> { /// description in error messages. This is used in the primary span label. Beyond what /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to /// ADTs with no type arguments. - pub fn is_simple_text(&self) -> bool { + pub fn is_simple_text(self) -> bool { match self.kind() { Adt(_, substs) => substs.non_erasable_generics().next().is_none(), Ref(_, ty, _) => ty.is_simple_text(), @@ -67,11 +67,11 @@ impl<'tcx> TyS<'tcx> { } /// Whether the type can be safely suggested during error recovery. - pub fn is_suggestable(&self) -> bool { + pub fn is_suggestable(self) -> bool { fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool { match arg.unpack() { GenericArgKind::Type(ty) => ty.is_suggestable(), - GenericArgKind::Const(c) => const_is_suggestable(c.val), + GenericArgKind::Const(c) => const_is_suggestable(c.val()), _ => true, } } @@ -105,8 +105,14 @@ impl<'tcx> TyS<'tcx> { ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => { substs.iter().all(generic_arg_is_suggestible) } - ExistentialPredicate::Projection(ExistentialProjection { substs, ty, .. }) => { - ty.is_suggestable() && substs.iter().all(generic_arg_is_suggestible) + ExistentialPredicate::Projection(ExistentialProjection { + substs, term, .. + }) => { + let term_is_suggestable = match term { + Term::Ty(ty) => ty.is_suggestable(), + Term::Const(c) => const_is_suggestable(c.val()), + }; + term_is_suggestable && substs.iter().all(generic_arg_is_suggestible) } _ => true, }), @@ -114,7 +120,7 @@ impl<'tcx> TyS<'tcx> { args.iter().all(generic_arg_is_suggestible) } Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(), - Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val), + Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()), _ => true, } } @@ -448,12 +454,6 @@ pub fn suggest_constraining_type_param( pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>); impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { - type Map = rustc_hir::intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::None - } - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { match ty.kind { hir::TyKind::TraitObject( @@ -482,12 +482,6 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { pub struct StaticLifetimeVisitor<'tcx>(pub Vec, pub crate::hir::map::Map<'tcx>); impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> { - type Map = rustc_hir::intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::None - } - fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) { if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static = lt.name diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index 0d290752e8..ef4f77c8a6 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -21,9 +21,7 @@ impl<'tcx> TyCtxt<'tcx> { T: TypeFoldable<'tcx>, { // If there's nothing to erase avoid performing the query at all - if !value - .has_type_flags(TypeFlags::HAS_RE_LATE_BOUND | TypeFlags::HAS_POTENTIAL_FREE_REGIONS) - { + if !value.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND | TypeFlags::HAS_FREE_REGIONS) { return value; } debug!("erase_regions({:?})", value); diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index df6e739dc2..2ccfeba2b6 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -60,13 +60,13 @@ pub enum TypeError<'tcx> { /// created a cycle (because it appears somewhere within that /// type). CyclicTy(Ty<'tcx>), - CyclicConst(&'tcx ty::Const<'tcx>), + CyclicConst(ty::Const<'tcx>), ProjectionMismatched(ExpectedFound), ExistentialMismatch( ExpectedFound<&'tcx ty::List>>>, ), ObjectUnsafeCoercion(DefId), - ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), + ConstMismatch(ExpectedFound>), IntrinsicCast, /// Safe `#[target_feature]` functions are not assignable to safe function pointers. @@ -239,8 +239,8 @@ impl<'tcx> TypeError<'tcx> { } } -impl<'tcx> ty::TyS<'tcx> { - pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { +impl<'tcx> Ty<'tcx> { + pub fn sort_string(self, tcx: TyCtxt<'_>) -> Cow<'static, str> { match *self.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { format!("`{}`", self).into() @@ -255,7 +255,7 @@ impl<'tcx> ty::TyS<'tcx> { } let n = tcx.lift(n).unwrap(); - if let ty::ConstKind::Value(v) = n.val { + if let ty::ConstKind::Value(v) = n.val() { if let Some(n) = v.try_to_machine_usize(tcx) { return format!("array of {} element{}", n, pluralize!(n)).into(); } @@ -306,7 +306,7 @@ impl<'tcx> ty::TyS<'tcx> { } } - pub fn prefix_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { + pub fn prefix_string(self, tcx: TyCtxt<'_>) -> Cow<'static, str> { match *self.kind() { ty::Infer(_) | ty::Error(_) @@ -869,7 +869,7 @@ fn foo(&self) -> Self::T { String::new() } // When `body_owner` is an `impl` or `trait` item, look in its associated types for // `expected` and point at it. let parent_id = self.hir().get_parent_item(hir_id); - let item = self.hir().find(parent_id); + let item = self.hir().find_by_def_id(parent_id); debug!("expected_projection parent item {:?}", item); match item { Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { @@ -972,10 +972,10 @@ fn foo(&self) -> Self::T { String::new() } let (span, sugg) = if has_params { let pos = span.hi() - BytePos(1); let span = Span::new(pos, pos, span.ctxt(), span.parent()); - (span, format!(", {} = {}", assoc.ident, ty)) + (span, format!(", {} = {}", assoc.ident(self), ty)) } else { let item_args = self.format_generic_args(assoc_substs); - (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident, item_args, ty)) + (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(self), item_args, ty)) }; db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); return true; diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index daf9156a15..983057bff9 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -54,12 +54,6 @@ pub enum SimplifyParams { No, } -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub enum StripReferences { - Yes, - No, -} - /// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists. /// /// The idea is to get something simple that we can use to quickly decide if two types could unify, @@ -73,8 +67,6 @@ pub enum StripReferences { /// When using `SimplifyParams::Yes`, we still return a simplified type for params and projections², /// the reasoning for this can be seen at the places doing this. /// -/// For diagnostics we strip references with `StripReferences::Yes`. This is currently the best -/// way to skip some unhelpful suggestions. /// /// ¹ meaning that if two outermost layers are different, then the whole types are also different. /// ² FIXME(@lcnr): this seems like it can actually end up being unsound with the way it's used during @@ -87,7 +79,6 @@ pub fn simplify_type( tcx: TyCtxt<'_>, ty: Ty<'_>, can_simplify_params: SimplifyParams, - strip_references: StripReferences, ) -> Option { match *ty.kind() { ty::Bool => Some(BoolSimplifiedType), @@ -106,16 +97,7 @@ pub fn simplify_type( } _ => Some(MarkerTraitObjectSimplifiedType), }, - ty::Ref(_, ty, mutbl) => { - if strip_references == StripReferences::Yes { - // For diagnostics, when recommending similar impls we want to - // recommend impls even when there is a reference mismatch, - // so we treat &T and T equivalently in that case. - simplify_type(tcx, ty, can_simplify_params, strip_references) - } else { - Some(RefSimplifiedType(mutbl)) - } - } + ty::Ref(_, _, mutbl) => Some(RefSimplifiedType(mutbl)), ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(ClosureSimplifiedType(def_id)), ty::Generator(def_id, _, _) => Some(GeneratorSimplifiedType(def_id)), ty::GeneratorWitness(ref tys) => { diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 617c522ac8..948a48c082 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -1,12 +1,12 @@ use crate::ty::subst::{GenericArg, GenericArgKind}; -use crate::ty::{self, InferConst, Ty, TypeFlags}; +use crate::ty::{self, InferConst, Term, Ty, TypeFlags}; use std::slice; #[derive(Debug)] pub struct FlagComputation { pub flags: TypeFlags, - // see `TyS::outer_exclusive_binder` for details + // see `Ty::outer_exclusive_binder` for details pub outer_exclusive_binder: ty::DebruijnIndex, } @@ -28,7 +28,7 @@ impl FlagComputation { result } - pub fn for_const(c: &ty::Const<'_>) -> TypeFlags { + pub fn for_const(c: ty::Const<'_>) -> TypeFlags { let mut result = FlagComputation::new(); result.add_const(c); result.flags @@ -97,7 +97,7 @@ impl FlagComputation { &ty::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), &ty::Param(_) => { - self.add_flags(TypeFlags::HAS_KNOWN_TY_PARAM); + self.add_flags(TypeFlags::HAS_TY_PARAM); self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); } @@ -241,9 +241,12 @@ impl FlagComputation { self.add_ty(a); self.add_ty(b); } - ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { + ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => { self.add_projection_ty(projection_ty); - self.add_ty(ty); + match term { + Term::Ty(ty) => self.add_ty(ty), + Term::Const(c) => self.add_const(c), + } } ty::PredicateKind::WellFormed(arg) => { self.add_substs(slice::from_ref(&arg)); @@ -267,7 +270,7 @@ impl FlagComputation { fn add_ty(&mut self, ty: Ty<'_>) { self.add_flags(ty.flags()); - self.add_exclusive_binder(ty.outer_exclusive_binder); + self.add_exclusive_binder(ty.outer_exclusive_binder()); } fn add_tys(&mut self, tys: &[Ty<'_>]) { @@ -283,9 +286,9 @@ impl FlagComputation { } } - fn add_const(&mut self, c: &ty::Const<'_>) { - self.add_ty(c.ty); - match c.val { + fn add_const(&mut self, c: ty::Const<'_>) { + self.add_ty(c.ty()); + match c.val() { ty::ConstKind::Unevaluated(unevaluated) => self.add_unevaluated_const(unevaluated), ty::ConstKind::Infer(infer) => { self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); @@ -298,7 +301,7 @@ impl FlagComputation { self.add_bound_var(debruijn); } ty::ConstKind::Param(_) => { - self.add_flags(TypeFlags::HAS_KNOWN_CT_PARAM); + self.add_flags(TypeFlags::HAS_CT_PARAM); self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); } ty::ConstKind::Placeholder(_) => { @@ -311,29 +314,16 @@ impl FlagComputation { } fn add_unevaluated_const

(&mut self, ct: ty::Unevaluated<'_, P>) { - // The generic arguments of unevaluated consts are a bit special, - // see the `rustc-dev-guide` for more information. - // - // FIXME(@lcnr): Actually add a link here. - if let Some(substs) = ct.substs_ { - // If they are available, we treat them as ordinary generic arguments. - self.add_substs(substs); - } else { - // Otherwise, we add `HAS_UNKNOWN_DEFAULT_CONST_SUBSTS` to signify - // that our const may potentially refer to generic parameters. - // - // Note that depending on which generic parameters are actually - // used in this constant, we may not actually refer to any generic - // parameters at all. - self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); - self.add_flags(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS); - } + self.add_substs(ct.substs); self.add_flags(TypeFlags::HAS_CT_PROJECTION); } fn add_existential_projection(&mut self, projection: &ty::ExistentialProjection<'_>) { self.add_substs(projection.substs); - self.add_ty(projection.ty); + match projection.term { + ty::Term::Ty(ty) => self.add_ty(ty), + ty::Term::Const(ct) => self.add_const(ct), + } } fn add_projection_ty(&mut self, projection_ty: ty::ProjectionTy<'_>) { diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index f5be8b21e8..4922d07ae1 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,38 +1,56 @@ -//! Generalized type folding mechanism. The setup is a bit convoluted -//! but allows for convenient usage. Let T be an instance of some -//! "foldable type" (one which implements `TypeFoldable`) and F be an -//! instance of a "folder" (a type which implements `TypeFolder`). Then -//! the setup is intended to be: +//! A generalized traversal mechanism for complex data structures that contain +//! type information. //! -//! T.fold_with(F) --calls--> F.fold_T(T) --calls--> T.super_fold_with(F) +//! There are two types of traversal. +//! - Folding. This is a modifying traversal. It consumes the data structure, +//! producing a (possibly) modified version of it. Both fallible and +//! infallible versions are available. The name is potentially +//! confusing, because this traversal is more like `Iterator::map` than +//! `Iterator::fold`. +//! - Visiting. This is a read-only traversal of the data structure. //! -//! This way, when you define a new folder F, you can override -//! `fold_T()` to customize the behavior, and invoke `T.super_fold_with()` -//! to get the original behavior. Meanwhile, to actually fold -//! something, you can just write `T.fold_with(F)`, which is -//! convenient. (Note that `fold_with` will also transparently handle -//! things like a `Vec` where T is foldable and so on.) +//! These traversals have limited flexibility. Only a small number of "types of +//! interest" within the complex data structures can receive custom +//! modification (when folding) or custom visitation (when visiting). These are +//! the ones containing the most important type-related information, such as +//! `Ty`, `Predicate`, `Region`, and `Const`. //! -//! In this ideal setup, the only function that actually *does* -//! anything is `T.super_fold_with()`, which traverses the type `T`. -//! Moreover, `T.super_fold_with()` should only ever call `T.fold_with()`. +//! There are two traits involved in each traversal type. +//! - The first trait is `TypeFoldable`, which is implemented once for many +//! types. This includes both (a) types of interest, and (b) all other +//! relevant types, including generic containers like `Vec` and `Option`. It +//! defines a "skeleton" of how they should be traversed, for both folding +//! and visiting. +//! - The second trait is `TypeFolder`/`FallibleTypeFolder` (for +//! infallible/fallible folding traversals) or `TypeVisitor` (for visiting +//! traversals). One of these is implemented for each folder/visitor. This +//! defines how types of interest are handled. //! -//! In some cases, we follow a degenerate pattern where we do not have -//! a `fold_T` method. Instead, `T.fold_with` traverses the structure directly. -//! This is suboptimal because the behavior cannot be overridden, but it's -//! much less work to implement. If you ever *do* need an override that -//! doesn't exist, it's not hard to convert the degenerate pattern into the -//! proper thing. +//! This means each traversal is a mixture of (a) generic traversal operations, +//! and (b) custom fold/visit operations that are specific to the +//! folder/visitor. +//! - The `TypeFoldable` impls handle most of the traversal, and call into +//! `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` when they encounter a +//! type of interest. +//! - A `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` may also call back into +//! a `TypeFoldable` impl, because (a) the types of interest are recursive +//! and can contain other types of interest, and (b) each folder/visitor +//! might provide custom handling only for some types of interest, or only +//! for some variants of each type of interest, and then use default +//! traversal for the remaining cases. //! -//! A `TypeFoldable` T can also be visited by a `TypeVisitor` V using similar setup: -//! -//! T.visit_with(V) --calls--> V.visit_T(T) --calls--> T.super_visit_with(V). -//! -//! These methods return true to indicate that the visitor has found what it is -//! looking for, and does not need to visit anything else. +//! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U: +//! TypeFoldable`, and an instance `S(ty, u)`, it would be visited like so: +//! ``` +//! s.visit_with(visitor) calls +//! - s.super_visit_with(visitor) calls +//! - ty.visit_with(visitor) calls +//! - visitor.visit_ty(ty) may call +//! - ty.super_visit_with(visitor) +//! - u.visit_with(visitor) +//! ``` use crate::mir; use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags}; -use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_data_structures::fx::FxHashSet; @@ -41,42 +59,67 @@ use std::collections::BTreeMap; use std::fmt; use std::ops::ControlFlow; -/// This trait is implemented for every type that can be folded. -/// Basically, every type that has a corresponding method in `TypeFolder`. +/// This trait is implemented for every type that can be folded/visited, +/// providing the skeleton of the traversal. /// -/// To implement this conveniently, use the derive macro located in `rustc_macros`. +/// To implement this conveniently, use the derive macro located in +/// `rustc_macros`. pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { - /// Consumers may find this more convenient to use with infallible folders than - /// [`try_super_fold_with`][`TypeFoldable::try_super_fold_with`], to which the - /// provided default definition delegates. Implementors **should not** override - /// this provided default definition, to ensure that the two methods are coherent - /// (provide a definition of `try_super_fold_with` instead). - fn super_fold_with>(self, folder: &mut F) -> Self { - self.try_super_fold_with(folder).into_ok() + /// The main entry point for folding. To fold a value `t` with a folder `f` + /// call: `t.try_fold_with(f)`. + /// + /// For types of interest (such as `Ty`), this default is overridden with a + /// method that calls a folder method specifically for that type (such as + /// `F::try_fold_ty`). This is where control transfers from `TypeFoldable` + /// to `TypeFolder`. + /// + /// For other types, this default is used. + fn try_fold_with>(self, folder: &mut F) -> Result { + self.try_super_fold_with(folder) } - /// Consumers may find this more convenient to use with infallible folders than - /// [`try_fold_with`][`TypeFoldable::try_fold_with`], to which the provided - /// default definition delegates. Implementors **should not** override this - /// provided default definition, to ensure that the two methods are coherent - /// (provide a definition of `try_fold_with` instead). + + /// A convenient alternative to `try_fold_with` for use with infallible + /// folders. Do not override this method, to ensure coherence with + /// `try_fold_with`. fn fold_with>(self, folder: &mut F) -> Self { self.try_fold_with(folder).into_ok() } + /// Traverses the type in question, typically by calling `try_fold_with` on + /// each field/element. This is true even for types of interest such as + /// `Ty`. This should only be called within `TypeFolder` methods, when + /// non-custom traversals are desired for types of interest. fn try_super_fold_with>( self, folder: &mut F, ) -> Result; - fn try_fold_with>(self, folder: &mut F) -> Result { - self.try_super_fold_with(folder) + /// A convenient alternative to `try_super_fold_with` for use with + /// infallible folders. Do not override this method, to ensure coherence + /// with `try_super_fold_with`. + fn super_fold_with>(self, folder: &mut F) -> Self { + self.try_super_fold_with(folder).into_ok() } - fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow; + /// The entry point for visiting. To visit a value `t` with a visitor `v` + /// call: `t.visit_with(v)`. + /// + /// For types of interest (such as `Ty`), this default is overridden with a + /// method that calls a visitor method specifically for that type (such as + /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to + /// `TypeVisitor`. + /// + /// For other types, this default is used. fn visit_with>(&self, visitor: &mut V) -> ControlFlow { self.super_visit_with(visitor) } + /// Traverses the type in question, typically by calling `visit_with` on + /// each field/element. This is true even for types of interest such as + /// `Ty`. This should only be called within `TypeVisitor` methods, when + /// non-custom traversals are desired for types of interest. + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow; + /// Returns `true` if `self` has any late-bound regions that are either /// bound by `binder` or bound by some binder outside of `binder`. /// If `binder` is `ty::INNERMOST`, this indicates whether @@ -95,15 +138,9 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { self.has_vars_bound_at_or_above(ty::INNERMOST) } - fn definitely_has_type_flags(&self, tcx: TyCtxt<'tcx>, flags: TypeFlags) -> bool { - self.visit_with(&mut HasTypeFlagsVisitor { tcx: Some(tcx), flags }).break_value() - == Some(FoundFlags) - } - #[instrument(level = "trace")] fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.visit_with(&mut HasTypeFlagsVisitor { tcx: None, flags }).break_value() - == Some(FoundFlags) + self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags) } fn has_projections(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PROJECTION) @@ -114,18 +151,8 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn references_error(&self) -> bool { self.has_type_flags(TypeFlags::HAS_ERROR) } - fn potentially_has_param_types_or_consts(&self) -> bool { - self.has_type_flags( - TypeFlags::HAS_KNOWN_TY_PARAM - | TypeFlags::HAS_KNOWN_CT_PARAM - | TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS, - ) - } - fn definitely_has_param_types_or_consts(&self, tcx: TyCtxt<'tcx>) -> bool { - self.definitely_has_type_flags( - tcx, - TypeFlags::HAS_KNOWN_TY_PARAM | TypeFlags::HAS_KNOWN_CT_PARAM, - ) + fn has_param_types_or_consts(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM) } fn has_infer_regions(&self) -> bool { self.has_type_flags(TypeFlags::HAS_RE_INFER) @@ -146,18 +173,13 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { | TypeFlags::HAS_CT_PLACEHOLDER, ) } - fn potentially_needs_subst(&self) -> bool { - self.has_type_flags( - TypeFlags::KNOWN_NEEDS_SUBST | TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS, - ) - } - fn definitely_needs_subst(&self, tcx: TyCtxt<'tcx>) -> bool { - self.definitely_has_type_flags(tcx, TypeFlags::KNOWN_NEEDS_SUBST) + fn needs_subst(&self) -> bool { + self.has_type_flags(TypeFlags::NEEDS_SUBST) } /// "Free" regions in this context means that it has any region /// that is not (a) erased or (b) late-bound. - fn has_free_regions(&self, tcx: TyCtxt<'tcx>) -> bool { - self.definitely_has_type_flags(tcx, TypeFlags::HAS_KNOWN_FREE_REGIONS) + fn has_free_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) } fn has_erased_regions(&self) -> bool { @@ -165,25 +187,15 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { } /// True if there are any un-erased free regions. - fn has_erasable_regions(&self, tcx: TyCtxt<'tcx>) -> bool { - self.definitely_has_type_flags(tcx, TypeFlags::HAS_KNOWN_FREE_REGIONS) - } - - /// Indicates whether this value definitely references only 'global' - /// generic parameters that are the same regardless of what fn we are - /// in. This is used for caching. - /// - /// Note that this function is pessimistic and may incorrectly return - /// `false`. - fn is_known_global(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_POTENTIAL_FREE_LOCAL_NAMES) + fn has_erasable_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) } /// Indicates whether this value references only 'global' /// generic parameters that are the same regardless of what fn we are /// in. This is used for caching. - fn is_global(&self, tcx: TyCtxt<'tcx>) -> bool { - !self.definitely_has_type_flags(tcx, TypeFlags::HAS_KNOWN_FREE_LOCAL_NAMES) + fn is_global(&self) -> bool { + !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES) } /// True if there are any late-bound regions @@ -199,24 +211,13 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { } } -impl<'tcx> TypeFoldable<'tcx> for hir::Constness { - fn try_super_fold_with>(self, _: &mut F) -> Result { - Ok(self) - } - fn super_visit_with>(&self, _: &mut V) -> ControlFlow { - ControlFlow::CONTINUE - } -} - -/// The `TypeFolder` trait defines the actual *folding*. There is a -/// method defined for every foldable type. Each of these has a -/// default implementation that does an "identity" fold. Within each -/// identity fold, it should invoke `foo.fold_with(self)` to fold each -/// sub-item. +/// This trait is implemented for every folding traversal. There is a fold +/// method defined for every type of interest. Each such method has a default +/// that does an "identity" fold. /// /// If this folder is fallible (and therefore its [`Error`][`TypeFolder::Error`] -/// associated type is something other than the default, never), -/// [`FallibleTypeFolder`] should be implemented manually; otherwise, +/// associated type is something other than the default `!`) then +/// [`FallibleTypeFolder`] should be implemented manually. Otherwise, /// a blanket implementation of [`FallibleTypeFolder`] will defer to /// the infallible methods of this trait to ensure that the two APIs /// are coherent. @@ -247,7 +248,7 @@ pub trait TypeFolder<'tcx>: Sized { r.super_fold_with(self) } - fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> where Self: TypeFolder<'tcx, Error = !>, { @@ -269,11 +270,9 @@ pub trait TypeFolder<'tcx>: Sized { } } -/// The `FallibleTypeFolder` trait defines the actual *folding*. There is a -/// method defined for every foldable type. Each of these has a -/// default implementation that does an "identity" fold. Within each -/// identity fold, it should invoke `foo.try_fold_with(self)` to fold each -/// sub-item. +/// This trait is implemented for every folding traversal. There is a fold +/// method defined for every type of interest. Each such method has a default +/// that does an "identity" fold. /// /// A blanket implementation of this trait (that defers to the relevant /// method of [`TypeFolder`]) is provided for all infallible folders in @@ -294,10 +293,7 @@ pub trait FallibleTypeFolder<'tcx>: TypeFolder<'tcx> { r.try_super_fold_with(self) } - fn try_fold_const( - &mut self, - c: &'tcx ty::Const<'tcx>, - ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { + fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result, Self::Error> { c.try_super_fold_with(self) } @@ -316,8 +312,8 @@ pub trait FallibleTypeFolder<'tcx>: TypeFolder<'tcx> { } } -// Blanket implementation of fallible trait for infallible folders -// delegates to infallible methods to prevent incoherence +// This blanket implementation of the fallible trait for infallible folders +// delegates to infallible methods to ensure coherence. impl<'tcx, F> FallibleTypeFolder<'tcx> for F where F: TypeFolder<'tcx, Error = !>, @@ -337,10 +333,7 @@ where Ok(self.fold_region(r)) } - fn try_fold_const( - &mut self, - c: &'tcx ty::Const<'tcx>, - ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { + fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result, Self::Error> { Ok(self.fold_const(c)) } @@ -359,19 +352,11 @@ where } } +/// This trait is implemented for every visiting traversal. There is a visit +/// method defined for every type of interest. Each such method has a default +/// that recurses into the type's fields in a non-custom fashion. pub trait TypeVisitor<'tcx>: Sized { type BreakTy = !; - /// Supplies the `tcx` for an unevaluated anonymous constant in case its default substs - /// are not yet supplied. - /// - /// Returning `None` for this method is only recommended if the `TypeVisitor` - /// does not care about default anon const substs, as it ignores generic parameters, - /// and fetching the default substs would cause a query cycle. - /// - /// For visitors which return `None` we completely skip the default substs in `ty::Unevaluated::super_visit_with`. - /// This means that incorrectly returning `None` can very quickly lead to ICE or other critical bugs, so be careful and - /// try to return an actual `tcx` if possible. - fn tcx_for_anon_const_substs(&self) -> Option>; fn visit_binder>( &mut self, @@ -388,7 +373,7 @@ pub trait TypeVisitor<'tcx>: Sized { r.super_visit_with(self) } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { c.super_visit_with(self) } @@ -408,7 +393,7 @@ pub struct BottomUpFolder<'tcx, F, G, H> where F: FnMut(Ty<'tcx>) -> Ty<'tcx>, G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>, - H: FnMut(&'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx>, + H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>, { pub tcx: TyCtxt<'tcx>, pub ty_op: F, @@ -420,7 +405,7 @@ impl<'tcx, F, G, H> TypeFolder<'tcx> for BottomUpFolder<'tcx, F, G, H> where F: FnMut(Ty<'tcx>) -> Ty<'tcx>, G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>, - H: FnMut(&'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx>, + H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>, { fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.tcx @@ -436,7 +421,7 @@ where (self.lt_op)(r) } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { let ct = ct.super_fold_with(self); (self.ct_op)(ct) } @@ -488,8 +473,7 @@ impl<'tcx> TyCtxt<'tcx> { value: &impl TypeFoldable<'tcx>, callback: impl FnMut(ty::Region<'tcx>) -> bool, ) -> bool { - struct RegionVisitor<'tcx, F> { - tcx: TyCtxt<'tcx>, + struct RegionVisitor { /// The index of a binder *just outside* the things we have /// traversed. If we encounter a bound region bound by this /// binder or one outer to it, it appears free. Example: @@ -511,16 +495,12 @@ impl<'tcx> TyCtxt<'tcx> { callback: F, } - impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor<'tcx, F> + impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor where F: FnMut(ty::Region<'tcx>) -> bool, { type BreakTy = (); - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - fn visit_binder>( &mut self, t: &Binder<'tcx, T>, @@ -548,7 +528,7 @@ impl<'tcx> TyCtxt<'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { // We're only interested in types involving regions - if ty.flags().intersects(TypeFlags::HAS_POTENTIAL_FREE_REGIONS) { + if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) { ty.super_visit_with(self) } else { ControlFlow::CONTINUE @@ -556,9 +536,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - value - .visit_with(&mut RegionVisitor { tcx: self, outer_index: ty::INNERMOST, callback }) - .is_break() + value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break() } } @@ -642,7 +620,7 @@ struct BoundVarReplacer<'a, 'tcx> { fld_r: Option<&'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a)>, fld_t: Option<&'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a)>, - fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx> + 'a)>, + fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a)>, } impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> { @@ -650,7 +628,7 @@ impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> { tcx: TyCtxt<'tcx>, fld_r: Option<&'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a)>, fld_t: Option<&'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a)>, - fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx> + 'a)>, + fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a)>, ) -> Self { BoundVarReplacer { tcx, current_index: ty::INNERMOST, fld_r, fld_t, fld_c } } @@ -676,7 +654,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => { if let Some(fld_t) = self.fld_t.as_mut() { let ty = fld_t(bound_ty); - return ty::fold::shift_vars(self.tcx, &ty, self.current_index.as_u32()); + return ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32()); } } _ if t.has_vars_bound_at_or_above(self.current_index) => { @@ -709,14 +687,12 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { r } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - match *ct { - ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty } - if debruijn == self.current_index => - { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.val() { + ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { if let Some(fld_c) = self.fld_c.as_mut() { - let ct = fld_c(bound_const, ty); - return ty::fold::shift_vars(self.tcx, &ct, self.current_index.as_u32()); + let ct = fld_c(bound_const, ct.ty()); + return ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32()); } } _ if ct.has_vars_bound_at_or_above(self.current_index) => { @@ -775,7 +751,7 @@ impl<'tcx> TyCtxt<'tcx> { where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, G: FnMut(ty::BoundTy) -> Ty<'tcx>, - H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>, + H: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>, T: TypeFoldable<'tcx>, { if !value.has_escaping_bound_vars() { @@ -800,7 +776,7 @@ impl<'tcx> TyCtxt<'tcx> { where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, G: FnMut(ty::BoundTy) -> Ty<'tcx>, - H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>, + H: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>, T: TypeFoldable<'tcx>, { let mut region_map = BTreeMap::new(); @@ -853,7 +829,7 @@ impl<'tcx> TyCtxt<'tcx> { )) }, |c, ty| { - self.mk_const(ty::Const { + self.mk_const(ty::ConstS { val: ty::ConstKind::Bound( ty::INNERMOST, ty::BoundVar::from_usize(c.as_usize() + bound_vars), @@ -897,7 +873,7 @@ impl<'tcx> TyCtxt<'tcx> { where T: TypeFoldable<'tcx>, { - let mut collector = LateBoundRegionsCollector::new(self, just_constraint); + let mut collector = LateBoundRegionsCollector::new(just_constraint); let result = value.as_ref().skip_binder().visit_with(&mut collector); assert!(result.is_continue()); // should never have stopped early collector.regions @@ -964,11 +940,6 @@ impl<'tcx> ValidateBoundVars<'tcx> { impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> { type BreakTy = (); - fn tcx_for_anon_const_substs(&self) -> Option> { - // Anonymous constants do not contain bound vars in their substs by default. - None - } - fn visit_binder>( &mut self, t: &Binder<'tcx, T>, @@ -980,7 +951,7 @@ impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> { } fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - if t.outer_exclusive_binder < self.binder_index + if t.outer_exclusive_binder() < self.binder_index || !self.visited.insert((self.binder_index, t)) { return ControlFlow::BREAK; @@ -1014,10 +985,10 @@ impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> { } fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - match r { - ty::ReLateBound(index, br) if *index == self.binder_index => { + match *r { + ty::ReLateBound(index, br) if index == self.binder_index => { if self.bound_vars.len() <= br.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", *br, self.bound_vars); + bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars); } let list_var = self.bound_vars[br.var.as_usize()]; match list_var { @@ -1111,13 +1082,16 @@ impl<'tcx> TypeFolder<'tcx> for Shifter<'tcx> { } } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { val: ty::ConstKind::Bound(debruijn, bound_ct), ty } = *ct { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Bound(debruijn, bound_ct) = ct.val() { if self.amount == 0 || debruijn < self.current_index { ct } else { let debruijn = debruijn.shifted_in(self.amount); - self.tcx.mk_const(ty::Const { val: ty::ConstKind::Bound(debruijn, bound_ct), ty }) + self.tcx.mk_const(ty::ConstS { + val: ty::ConstKind::Bound(debruijn, bound_ct), + ty: ct.ty(), + }) } } else { ct.super_fold_with(self) @@ -1130,9 +1104,9 @@ pub fn shift_region<'tcx>( region: ty::Region<'tcx>, amount: u32, ) -> ty::Region<'tcx> { - match region { + match *region { ty::ReLateBound(debruijn, br) if amount > 0 => { - tcx.mk_region(ty::ReLateBound(debruijn.shifted_in(amount), *br)) + tcx.mk_region(ty::ReLateBound(debruijn.shifted_in(amount), br)) } _ => region, } @@ -1183,11 +1157,6 @@ struct HasEscapingVarsVisitor { impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { type BreakTy = FoundEscapingVars; - fn tcx_for_anon_const_substs(&self) -> Option> { - // Anonymous constants do not contain bound vars in their substs by default. - None - } - fn visit_binder>( &mut self, t: &Binder<'tcx, T>, @@ -1205,7 +1174,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { // bound at `outer_index` or above (because // `outer_exclusive_binder` is always 1 higher than the // content in `t`). Therefore, `t` has some escaping vars. - if t.outer_exclusive_binder > self.outer_index { + if t.outer_exclusive_binder() > self.outer_index { ControlFlow::Break(FoundEscapingVars) } else { ControlFlow::CONTINUE @@ -1224,13 +1193,13 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { } } - fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> ControlFlow { + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { // we don't have a `visit_infer_const` callback, so we have to // hook in here to catch this case (annoying...), but // otherwise we do want to remember to visit the rest of the // const, as it has types/regions embedded in a lot of other // places. - match ct.val { + match ct.val() { ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => { ControlFlow::Break(FoundEscapingVars) } @@ -1240,7 +1209,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { #[inline] fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { - if predicate.inner.outer_exclusive_binder > self.outer_index { + if predicate.outer_exclusive_binder() > self.outer_index { ControlFlow::Break(FoundEscapingVars) } else { ControlFlow::CONTINUE @@ -1252,35 +1221,32 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { struct FoundFlags; // FIXME: Optimize for checking for infer flags -struct HasTypeFlagsVisitor<'tcx> { - tcx: Option>, +struct HasTypeFlagsVisitor { flags: ty::TypeFlags, } -impl<'tcx> std::fmt::Debug for HasTypeFlagsVisitor<'tcx> { +impl std::fmt::Debug for HasTypeFlagsVisitor { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.flags.fmt(fmt) } } -impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor<'tcx> { +impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { type BreakTy = FoundFlags; - fn tcx_for_anon_const_substs(&self) -> Option> { - bug!("we shouldn't call this method as we manually look at ct substs"); - } #[inline] #[instrument(level = "trace")] - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - let flags = t.flags(); - trace!(t.flags=?t.flags()); - if flags.intersects(self.flags) { + fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow { + debug!( + "HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}", + t, + t.flags(), + self.flags + ); + if t.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { - match flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) { - true if self.tcx.is_some() => UnknownConstSubstsVisitor::search(&self, t), - _ => ControlFlow::CONTINUE, - } + ControlFlow::CONTINUE } } @@ -1298,16 +1264,13 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor<'tcx> { #[inline] #[instrument(level = "trace")] - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { let flags = FlagComputation::for_const(c); trace!(r.flags=?flags); if flags.intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { - match flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) { - true if self.tcx.is_some() => UnknownConstSubstsVisitor::search(&self, c), - _ => ControlFlow::CONTINUE, - } + ControlFlow::CONTINUE } } @@ -1319,128 +1282,30 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor<'tcx> { if flags.intersects(self.flags) { ControlFlow::Break(FoundFlags) } else { - match flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) { - true if self.tcx.is_some() => UnknownConstSubstsVisitor::search(&self, uv), - _ => ControlFlow::CONTINUE, - } + ControlFlow::CONTINUE } } #[inline] #[instrument(level = "trace")] fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { - let flags = predicate.inner.flags; - trace!(predicate.flags=?flags); - if flags.intersects(self.flags) { + debug!( + "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", + predicate, + predicate.flags(), + self.flags + ); + if predicate.flags().intersects(self.flags) { ControlFlow::Break(FoundFlags) - } else { - match flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) { - true if self.tcx.is_some() => UnknownConstSubstsVisitor::search(&self, predicate), - _ => ControlFlow::CONTINUE, - } - } - } -} - -struct UnknownConstSubstsVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - flags: ty::TypeFlags, -} - -impl<'tcx> UnknownConstSubstsVisitor<'tcx> { - /// This is fairly cold and we don't want to - /// bloat the size of the `HasTypeFlagsVisitor`. - #[inline(never)] - pub fn search>( - visitor: &HasTypeFlagsVisitor<'tcx>, - v: T, - ) -> ControlFlow { - if visitor.flags.intersects(TypeFlags::MAY_NEED_DEFAULT_CONST_SUBSTS) { - v.super_visit_with(&mut UnknownConstSubstsVisitor { - tcx: visitor.tcx.unwrap(), - flags: visitor.flags, - }) - } else { - ControlFlow::CONTINUE - } - } -} - -impl<'tcx> TypeVisitor<'tcx> for UnknownConstSubstsVisitor<'tcx> { - type BreakTy = FoundFlags; - fn tcx_for_anon_const_substs(&self) -> Option> { - bug!("we shouldn't call this method as we manually look at ct substs"); - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - if t.flags().intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) { - t.super_visit_with(self) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - fn visit_unevaluated_const(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow { - if uv.substs_.is_none() { - self.tcx - .default_anon_const_substs(uv.def.did) - .visit_with(&mut HasTypeFlagsVisitor { tcx: Some(self.tcx), flags: self.flags }) - } else { - ControlFlow::CONTINUE - } - } - - #[inline] - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { - if predicate.inner.flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) { - predicate.super_visit_with(self) } else { ControlFlow::CONTINUE } } } -impl<'tcx> TyCtxt<'tcx> { - /// This is a HACK(const_generics) and should probably not be needed. - /// Might however be perf relevant, so who knows. - /// - /// FIXME(@lcnr): explain this function a bit more - pub fn expose_default_const_substs>(self, v: T) -> T { - v.fold_with(&mut ExposeDefaultConstSubstsFolder { tcx: self }) - } -} - -struct ExposeDefaultConstSubstsFolder<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> TypeFolder<'tcx> for ExposeDefaultConstSubstsFolder<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.flags().intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) { - ty.super_fold_with(self) - } else { - ty - } - } - - fn fold_predicate(&mut self, pred: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if pred.inner.flags.intersects(TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS) { - pred.super_fold_with(self) - } else { - pred - } - } -} - /// Collects all the late-bound regions at the innermost binding level /// into a hash set. -struct LateBoundRegionsCollector<'tcx> { - tcx: TyCtxt<'tcx>, +struct LateBoundRegionsCollector { current_index: ty::DebruijnIndex, regions: FxHashSet, @@ -1454,10 +1319,9 @@ struct LateBoundRegionsCollector<'tcx> { just_constrained: bool, } -impl<'tcx> LateBoundRegionsCollector<'tcx> { - fn new(tcx: TyCtxt<'tcx>, just_constrained: bool) -> Self { +impl LateBoundRegionsCollector { + fn new(just_constrained: bool) -> Self { LateBoundRegionsCollector { - tcx, current_index: ty::INNERMOST, regions: Default::default(), just_constrained, @@ -1465,11 +1329,7 @@ impl<'tcx> LateBoundRegionsCollector<'tcx> { } } -impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - +impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { fn visit_binder>( &mut self, t: &Binder<'tcx, T>, @@ -1493,12 +1353,12 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector<'tcx> { t.super_visit_with(self) } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { // if we are only looking for "constrained" region, we have to // ignore the inputs of an unevaluated const, as they may not appear // in the normalized form if self.just_constrained { - if let ty::ConstKind::Unevaluated(..) = c.val { + if let ty::ConstKind::Unevaluated(..) = c.val() { return ControlFlow::CONTINUE; } } diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 1c3a01e2cf..0bd96f8f86 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -31,6 +31,13 @@ impl GenericParamDefKind { GenericParamDefKind::Const { .. } => ast::ParamKindOrd::Const, } } + + pub fn is_ty_or_const(&self) -> bool { + match self { + GenericParamDefKind::Lifetime => false, + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => true, + } + } } #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 9f47ed89f1..00ce15bea3 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -6,6 +6,7 @@ use crate::mir; use crate::ty; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_query_system::ich::StableHashingContext; use std::cell::RefCell; @@ -17,12 +18,12 @@ where { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { - static CACHE: RefCell> = + static CACHE: RefCell> = RefCell::new(Default::default()); } let hash = CACHE.with(|cache| { - let key = (self.as_ptr() as usize, self.len()); + let key = (self.as_ptr() as usize, self.len(), hcx.hashing_controls()); if let Some(&hash) = cache.borrow().get(&key) { return hash; } 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 f31c7dd743..c4ad698ba7 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs @@ -1,9 +1,8 @@ use crate::ty::context::TyCtxt; use crate::ty::{DefId, DefIdTree}; -use rustc_hir::CRATE_HIR_ID; +use rustc_span::def_id::CRATE_DEF_ID; use smallvec::SmallVec; use std::mem; -use std::sync::Arc; use DefIdForest::*; @@ -18,14 +17,13 @@ use DefIdForest::*; /// 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 { +#[derive(Copy, Clone, HashStable, Debug)] +pub enum DefIdForest<'a> { 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]>), + Multiple(&'a [DefId]), } /// Tests whether a slice of roots contains a given DefId. @@ -34,21 +32,21 @@ fn slice_contains<'tcx>(tcx: TyCtxt<'tcx>, slice: &[DefId], id: DefId) -> bool { slice.iter().any(|root_id| tcx.is_descendant_of(id, *root_id)) } -impl<'tcx> DefIdForest { +impl<'tcx> DefIdForest<'tcx> { /// Creates an empty forest. - pub fn empty() -> DefIdForest { + pub fn empty() -> DefIdForest<'tcx> { DefIdForest::Empty } /// Creates a forest consisting of a single tree representing the entire /// crate. #[inline] - pub fn full(tcx: TyCtxt<'tcx>) -> DefIdForest { - DefIdForest::from_id(tcx.hir().local_def_id(CRATE_HIR_ID).to_def_id()) + pub fn full() -> DefIdForest<'tcx> { + DefIdForest::from_id(CRATE_DEF_ID.to_def_id()) } /// Creates a forest containing a `DefId` and all its descendants. - pub fn from_id(id: DefId) -> DefIdForest { + pub fn from_id(id: DefId) -> DefIdForest<'tcx> { DefIdForest::Single(id) } @@ -61,11 +59,11 @@ impl<'tcx> DefIdForest { } // Only allocates in the rare `Multiple` case. - fn from_slice(root_ids: &[DefId]) -> DefIdForest { - match root_ids { + fn from_vec(tcx: TyCtxt<'tcx>, root_ids: SmallVec<[DefId; 1]>) -> DefIdForest<'tcx> { + match &root_ids[..] { [] => Empty, [id] => Single(*id), - _ => DefIdForest::Multiple(root_ids.into()), + _ => DefIdForest::Multiple(tcx.arena.alloc_from_iter(root_ids)), } } @@ -88,15 +86,15 @@ impl<'tcx> DefIdForest { } /// Calculate the intersection of a collection of forests. - pub fn intersection(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest + pub fn intersection(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest<'tcx> where - I: IntoIterator, + I: IntoIterator>, { let mut iter = iter.into_iter(); let mut ret: SmallVec<[_; 1]> = if let Some(first) = iter.next() { SmallVec::from_slice(first.as_slice()) } else { - return DefIdForest::full(tcx); + return DefIdForest::full(); }; let mut next_ret: SmallVec<[_; 1]> = SmallVec::new(); @@ -114,13 +112,13 @@ impl<'tcx> DefIdForest { mem::swap(&mut next_ret, &mut ret); next_ret.clear(); } - DefIdForest::from_slice(&ret) + DefIdForest::from_vec(tcx, ret) } /// Calculate the union of a collection of forests. - pub fn union(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest + pub fn union(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest<'tcx> where - I: IntoIterator, + I: IntoIterator>, { let mut ret: SmallVec<[_; 1]> = SmallVec::new(); let mut next_ret: SmallVec<[_; 1]> = SmallVec::new(); @@ -142,6 +140,6 @@ impl<'tcx> DefIdForest { mem::swap(&mut next_ret, &mut ret); next_ret.clear(); } - DefIdForest::from_slice(&ret) + DefIdForest::from_vec(tcx, ret) } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 77d82ee6ea..f2682b8bcd 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -3,7 +3,7 @@ pub use self::def_id_forest::DefIdForest; use crate::ty; use crate::ty::context::TyCtxt; use crate::ty::TyKind::*; -use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef}; +use crate::ty::{AdtDef, FieldDef, Ty, VariantDef}; use crate::ty::{AdtKind, Visibility}; use crate::ty::{DefId, SubstsRef}; @@ -112,7 +112,7 @@ impl<'tcx> AdtDef { tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { + ) -> DefIdForest<'tcx> { // Non-exhaustive ADTs from other crates are always considered inhabited. if self.is_variant_list_non_exhaustive() && !self.did.is_local() { DefIdForest::empty() @@ -135,7 +135,7 @@ impl<'tcx> VariantDef { substs: SubstsRef<'tcx>, adt_kind: AdtKind, param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { + ) -> DefIdForest<'tcx> { let is_enum = match adt_kind { // For now, `union`s are never considered uninhabited. // The precise semantics of inhabitedness with respect to unions is currently undecided. @@ -163,7 +163,7 @@ impl<'tcx> FieldDef { substs: SubstsRef<'tcx>, is_enum: bool, param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { + ) -> DefIdForest<'tcx> { let data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(tcx, param_env); // FIXME(canndrew): Currently enum fields are (incorrectly) stored with // `Visibility::Invisible` so we need to override `self.vis` if we're @@ -184,14 +184,14 @@ impl<'tcx> FieldDef { } } -impl<'tcx> TyS<'tcx> { +impl<'tcx> Ty<'tcx> { /// Calculates the forest of `DefId`s from which this type is visibly uninhabited. fn uninhabited_from( - &'tcx self, + self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - ) -> DefIdForest { - tcx.type_uninhabited_from(param_env.and(self)) + ) -> DefIdForest<'tcx> { + tcx.type_uninhabited_from(param_env.and(self)).clone() } } @@ -199,13 +199,13 @@ impl<'tcx> TyS<'tcx> { pub(crate) fn type_uninhabited_from<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> DefIdForest { +) -> DefIdForest<'tcx> { let ty = key.value; let param_env = key.param_env; match *ty.kind() { Adt(def, substs) => def.uninhabited_from(tcx, substs, param_env), - Never => DefIdForest::full(tcx), + Never => DefIdForest::full(), Tuple(ref tys) => DefIdForest::union( tcx, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index eaa7ee84b7..99c595fcdf 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -101,7 +101,7 @@ impl<'tcx> Instance<'tcx> { /// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization. pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { let ty = tcx.type_of(self.def.def_id()); - tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty) + tcx.subst_and_normalize_erasing_regions(self.substs, param_env, ty) } /// Finds a crate that contains a monomorphization of this instance that @@ -642,7 +642,7 @@ fn polymorphize<'tcx>( fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { debug!("fold_ty: ty={:?}", ty); - match ty.kind { + match *ty.kind() { ty::Closure(def_id, substs) => { let polymorphized_substs = polymorphize( self.tcx, diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 196fe7ce1b..6d4178c3e7 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -10,7 +10,7 @@ use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; use rustc_session::{config::OptLevel, DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::call::{ ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind, @@ -1310,7 +1310,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { }, }; let mut abi = Abi::Aggregate { sized: true }; - if tag.value.size(dl) == size { + + // Without latter check aligned enums with custom discriminant values + // Would result in ICE see the issue #92464 for more info + if tag.value.size(dl) == size || variants.iter().all(|layout| layout.is_empty()) { abi = Abi::Scalar(tag); } else { // Try to use a ScalarPair for all tagged enums. @@ -1769,9 +1772,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // Ignore layouts that are done with non-empty environments or // non-monomorphic layouts, as the user only wants to see the stuff // resulting from the final codegen session. - if layout.ty.definitely_has_param_types_or_consts(self.tcx) - || !self.param_env.caller_bounds().is_empty() - { + if layout.ty.has_param_types_or_consts() || !self.param_env.caller_bounds().is_empty() { return; } @@ -1810,7 +1811,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let adt_kind = adt_def.adt_kind(); let adt_packed = adt_def.repr.pack.is_some(); - let build_variant_info = |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { + let build_variant_info = |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { let mut min_size = Size::ZERO; let field_info: Vec<_> = flds .iter() @@ -1845,15 +1846,15 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { if !adt_def.variants.is_empty() && layout.fields != FieldsShape::Primitive { debug!( "print-type-size `{:#?}` variant {}", - layout, adt_def.variants[index].ident + layout, adt_def.variants[index].name ); let variant_def = &adt_def.variants[index]; - let fields: Vec<_> = variant_def.fields.iter().map(|f| f.ident.name).collect(); + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); record( adt_kind.into(), adt_packed, None, - vec![build_variant_info(Some(variant_def.ident), &fields, layout)], + vec![build_variant_info(Some(variant_def.name), &fields, layout)], ); } else { // (This case arises for *empty* enums; so give it @@ -1872,10 +1873,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { .variants .iter_enumerated() .map(|(i, variant_def)| { - let fields: Vec<_> = - variant_def.fields.iter().map(|f| f.ident.name).collect(); + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); build_variant_info( - Some(variant_def.ident), + Some(variant_def.name), &fields, layout.for_variant(self, i), ) @@ -1937,7 +1937,7 @@ impl<'tcx> SizeSkeleton<'tcx> { let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); match tail.kind() { ty::Param(_) | ty::Projection(_) => { - debug_assert!(tail.definitely_has_param_types_or_consts(tcx)); + debug_assert!(tail.has_param_types_or_consts()); Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) }) } _ => bug!( @@ -2776,17 +2776,20 @@ pub fn fn_can_unwind<'tcx>( // [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md use SpecAbi::*; match abi { - C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => { + C { unwind } + | System { unwind } + | Cdecl { unwind } + | Stdcall { unwind } + | Fastcall { unwind } + | Vectorcall { unwind } + | Thiscall { unwind } + | Aapcs { unwind } + | Win64 { unwind } + | SysV64 { unwind } => { unwind || (!tcx.features().c_unwind && tcx.sess.panic_strategy() == PanicStrategy::Unwind) } - Cdecl - | Fastcall - | Vectorcall - | Aapcs - | Win64 - | SysV64 - | PtxKernel + PtxKernel | Msp430Interrupt | X86Interrupt | AmdGpuKernel @@ -2813,14 +2816,14 @@ pub fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv { EfiApi => bug!("eficall abi should be selected elsewhere"), Stdcall { .. } => Conv::X86Stdcall, - Fastcall => Conv::X86Fastcall, - Vectorcall => Conv::X86VectorCall, + Fastcall { .. } => Conv::X86Fastcall, + Vectorcall { .. } => Conv::X86VectorCall, Thiscall { .. } => Conv::X86ThisCall, C { .. } => Conv::C, Unadjusted => Conv::C, - Win64 => Conv::X86_64Win64, - SysV64 => Conv::X86_64SysV, - Aapcs => Conv::ArmAapcs, + Win64 { .. } => Conv::X86_64Win64, + SysV64 { .. } => Conv::X86_64SysV, + Aapcs { .. } => Conv::ArmAapcs, CCmseNonSecureCall => Conv::CCmseNonSecureCall, PtxKernel => Conv::PtxKernel, Msp430Interrupt => Conv::Msp430Intr, @@ -2831,12 +2834,12 @@ pub fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv { Wasm => Conv::C, // These API constants ought to be more specific... - Cdecl => Conv::C, + Cdecl { .. } => Conv::C, } } /// Error produced by attempting to compute or adjust a `FnAbi`. -#[derive(Clone, Debug, HashStable)] +#[derive(Copy, Clone, Debug, HashStable)] pub enum FnAbiError<'tcx> { /// Error produced by a `layout_of` call, while computing `FnAbi` initially. Layout(LayoutError<'tcx>), @@ -3048,9 +3051,10 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { layout: TyAndLayout<'tcx>, offset: Size, is_return: bool| { - // Booleans are always an i1 that needs to be zero-extended. + // Booleans are always a noundef i1 that needs to be zero-extended. if scalar.is_bool() { attrs.ext(ArgExtension::Zext); + attrs.set(ArgAttribute::NoUndef); return; } @@ -3075,6 +3079,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { _ => pointee.size, }; + // `Box`, `&T`, and `&mut T` cannot be undef. + // Note that this only applies to the value of the pointer itself; + // this attribute doesn't make it UB for the pointed-to data to be undef. + attrs.set(ArgAttribute::NoUndef); + // `Box` pointer parameters never alias because ownership is transferred // `&mut` pointer parameters never alias other parameters, // or mutable global data diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 78ccfbd5e8..f0b7f2a653 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -19,7 +19,8 @@ pub use assoc::*; pub use generics::*; pub use vtable::*; -use crate::hir::exports::ExportMap; +use crate::metadata::ModChild; +use crate::middle::privacy::AccessLevels; use crate::mir::{Body, GeneratorLayout}; use crate::traits::{self, Reveal}; use crate::ty; @@ -27,7 +28,8 @@ use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; use crate::ty::util::Discr; use rustc_ast as ast; use rustc_attr as attr; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::tagged_ptr::CopyTaggedPtr; use rustc_hir as hir; @@ -41,11 +43,9 @@ use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span}; use rustc_target::abi::Align; -use std::cmp::Ordering; -use std::collections::BTreeMap; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::ops::ControlFlow; -use std::{fmt, ptr, str}; +use std::{fmt, str}; pub use crate::ty::diagnostics::*; pub use rustc_type_ir::InferTy::*; @@ -56,10 +56,12 @@ pub use self::binding::BindingMode::*; pub use self::closure::{ is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo, CapturedPlace, ClosureKind, MinCaptureInformationMap, MinCaptureList, - RootVariableMinCaptureList, UpvarBorrow, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, - UpvarPath, CAPTURE_STRUCT_LOCAL, + RootVariableMinCaptureList, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, UpvarPath, + CAPTURE_STRUCT_LOCAL, +}; +pub use self::consts::{ + Const, ConstInt, ConstKind, ConstS, InferConst, ScalarInt, Unevaluated, ValTree, }; -pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree}; pub use self::context::{ tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt, @@ -118,27 +120,31 @@ mod sty; // Data types +pub type RegisteredTools = FxHashSet; + #[derive(Debug)] pub struct ResolverOutputs { pub definitions: rustc_hir::definitions::Definitions, pub cstore: Box, pub visibilities: FxHashMap, + pub access_levels: AccessLevels, pub extern_crate_map: FxHashMap, pub maybe_unused_trait_imports: FxHashSet, pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, - pub export_map: ExportMap, + pub reexport_map: FxHashMap>, pub glob_map: FxHashMap>, /// Extern prelude entries. The value is `true` if the entry was introduced /// via `extern crate` item and not `--extern` option or compiler built-in. pub extern_prelude: FxHashMap, pub main_def: Option, - pub trait_impls: BTreeMap>, + pub trait_impls: FxIndexMap>, /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. pub proc_macros: Vec, /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: FxHashMap, + pub registered_tools: RegisteredTools, } #[derive(Clone, Copy, Debug)] @@ -374,15 +380,32 @@ pub struct CReaderCacheKey { pub pos: usize, } +/// Represents a type. +/// +/// IMPORTANT: +/// - This is a very "dumb" struct (with no derives and no `impls`). +/// - Values of this type are always interned and thus unique, and are stored +/// as an `Interned`. +/// - `Ty` (which contains a reference to a `Interned`) or `Interned` +/// should be used everywhere instead of `TyS`. In particular, `Ty` has most +/// of the relevant methods. +#[derive(PartialEq, Eq, PartialOrd, Ord)] #[allow(rustc::usage_of_ty_tykind)] -pub struct TyS<'tcx> { +crate struct TyS<'tcx> { /// This field shouldn't be used directly and may be removed in the future. - /// Use `TyS::kind()` instead. + /// Use `Ty::kind()` instead. kind: TyKind<'tcx>, + + /// This field provides fast access to information that is also contained + /// in `kind`. + /// /// This field shouldn't be used directly and may be removed in the future. - /// Use `TyS::flags()` instead. + /// Use `Ty::flags()` instead. flags: TypeFlags, + /// This field provides fast access to information that is also contained + /// in `kind`. + /// /// This is a kind of confusing thing: it stores the smallest /// binder such that /// @@ -403,51 +426,27 @@ pub struct TyS<'tcx> { outer_exclusive_binder: ty::DebruijnIndex, } -impl<'tcx> TyS<'tcx> { - /// A constructor used only for internal testing. - #[allow(rustc::usage_of_ty_tykind)] - pub fn make_for_test( - kind: TyKind<'tcx>, - flags: TypeFlags, - outer_exclusive_binder: ty::DebruijnIndex, - ) -> TyS<'tcx> { - TyS { kind, flags, outer_exclusive_binder } - } -} - // `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(TyS<'_>, 40); -impl<'tcx> Ord for TyS<'tcx> { - fn cmp(&self, other: &TyS<'tcx>) -> Ordering { - self.kind().cmp(other.kind()) - } -} - -impl<'tcx> PartialOrd for TyS<'tcx> { - fn partial_cmp(&self, other: &TyS<'tcx>) -> Option { - Some(self.kind().cmp(other.kind())) - } -} - -impl<'tcx> PartialEq for TyS<'tcx> { - #[inline] - fn eq(&self, other: &TyS<'tcx>) -> bool { - ptr::eq(self, other) - } -} -impl<'tcx> Eq for TyS<'tcx> {} - -impl<'tcx> Hash for TyS<'tcx> { - fn hash(&self, s: &mut H) { - (self as *const TyS<'_>).hash(s) - } -} +/// Use this rather than `TyS`, whenever possible. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[rustc_diagnostic_item = "Ty"] +#[cfg_attr(not(bootstrap), rustc_pass_by_value)] +pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>); + +// Statics only used for internal testing. +pub static BOOL_TY: Ty<'static> = Ty(Interned::new_unchecked(&BOOL_TYS)); +static BOOL_TYS: TyS<'static> = TyS { + kind: ty::Bool, + flags: TypeFlags::empty(), + outer_exclusive_binder: DebruijnIndex::from_usize(0), +}; -impl<'a, 'tcx> HashStable> for TyS<'tcx> { +impl<'a, 'tcx> HashStable> for Ty<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ty::TyS { + let TyS { ref kind, // The other fields just provide fast access to information that is @@ -455,15 +454,12 @@ impl<'a, 'tcx> HashStable> for TyS<'tcx> { flags: _, outer_exclusive_binder: _, - } = *self; + } = self.0.0; kind.hash_stable(hcx, hasher); } } -#[rustc_diagnostic_item = "Ty"] -pub type Ty<'tcx> = &'tcx TyS<'tcx>; - impl ty::EarlyBoundRegion { /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). @@ -472,51 +468,50 @@ impl ty::EarlyBoundRegion { } } +/// Represents a predicate. +/// +/// See comments on `TyS`, which apply here too (albeit for +/// `PredicateS`/`Predicate` rather than `TyS`/`Ty`). #[derive(Debug)] -crate struct PredicateInner<'tcx> { +crate struct PredicateS<'tcx> { kind: Binder<'tcx, PredicateKind<'tcx>>, flags: TypeFlags, /// See the comment for the corresponding field of [TyS]. outer_exclusive_binder: ty::DebruijnIndex, } +// This type is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(PredicateInner<'_>, 48); +static_assert_size!(PredicateS<'_>, 56); -#[derive(Clone, Copy, Lift)] -pub struct Predicate<'tcx> { - inner: &'tcx PredicateInner<'tcx>, -} - -impl<'tcx> PartialEq for Predicate<'tcx> { - fn eq(&self, other: &Self) -> bool { - // `self.kind` is always interned. - ptr::eq(self.inner, other.inner) - } -} - -impl Hash for Predicate<'_> { - fn hash(&self, s: &mut H) { - (self.inner as *const PredicateInner<'_>).hash(s) - } -} - -impl<'tcx> Eq for Predicate<'tcx> {} +/// Use this rather than `PredicateS`, whenever possible. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(not(bootstrap), rustc_pass_by_value)] +pub struct Predicate<'tcx>(Interned<'tcx, PredicateS<'tcx>>); impl<'tcx> Predicate<'tcx> { /// Gets the inner `Binder<'tcx, PredicateKind<'tcx>>`. #[inline] pub fn kind(self) -> Binder<'tcx, PredicateKind<'tcx>> { - self.inner.kind + self.0.kind + } + + #[inline(always)] + pub fn flags(self) -> TypeFlags { + self.0.flags + } + + #[inline(always)] + pub fn outer_exclusive_binder(self) -> DebruijnIndex { + self.0.outer_exclusive_binder } /// Flips the polarity of a Predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(&self, tcx: TyCtxt<'tcx>) -> Option> { + pub fn flip_polarity(self, tcx: TyCtxt<'tcx>) -> Option> { let kind = self - .inner - .kind + .kind() .map_bound(|kind| match kind { PredicateKind::Trait(TraitPredicate { trait_ref, constness, polarity }) => { Some(PredicateKind::Trait(TraitPredicate { @@ -536,14 +531,14 @@ impl<'tcx> Predicate<'tcx> { impl<'a, 'tcx> HashStable> for Predicate<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let PredicateInner { + let PredicateS { ref kind, // The other fields just provide fast access to information that is // also contained in `kind`, so no need to hash them. flags: _, outer_exclusive_binder: _, - } = self.inner; + } = self.0.0; kind.hash_stable(hcx, hasher); } @@ -599,7 +594,7 @@ pub enum PredicateKind<'tcx> { ConstEvaluatable(ty::Unevaluated<'tcx, ()>), /// Constants must be equal. The first component is the const that is expected. - ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>), + ConstEquate(Const<'tcx>, Const<'tcx>), /// Represents a type found in the environment that we can use for implied bounds. /// @@ -743,6 +738,17 @@ impl<'tcx> TraitPredicate<'tcx> { *param_env = param_env.with_constness(self.constness.and(param_env.constness())) } } + + /// Remap the constness of this predicate before emitting it for diagnostics. + pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) { + // this is different to `remap_constness` that callees want to print this predicate + // in case of selection errors. `T: ~const Drop` bounds cannot end up here when the + // param_env is not const because we it is always satisfied in non-const contexts. + if let hir::Constness::NotConst = param_env.constness() { + self.constness = ty::BoundConstness::NotConst; + } + } + pub fn def_id(self) -> DefId { self.trait_ref.def_id } @@ -750,6 +756,11 @@ impl<'tcx> TraitPredicate<'tcx> { pub fn self_ty(self) -> Ty<'tcx> { self.trait_ref.self_ty() } + + #[inline] + pub fn is_const_if_const(self) -> bool { + self.constness == BoundConstness::ConstIfConst + } } impl<'tcx> PolyTraitPredicate<'tcx> { @@ -761,6 +772,19 @@ impl<'tcx> PolyTraitPredicate<'tcx> { pub fn self_ty(self) -> ty::Binder<'tcx, Ty<'tcx>> { self.map_bound(|trait_ref| trait_ref.self_ty()) } + + /// Remap the constness of this predicate before emitting it for diagnostics. + pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) { + *self = self.map_bound(|mut p| { + p.remap_constness_diag(param_env); + p + }); + } + + #[inline] + pub fn is_const_if_const(self) -> bool { + self.skip_binder().is_const_if_const() + } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] @@ -792,6 +816,34 @@ pub struct CoercePredicate<'tcx> { } pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(HashStable, TypeFoldable)] +pub enum Term<'tcx> { + Ty(Ty<'tcx>), + Const(Const<'tcx>), +} + +impl<'tcx> From> for Term<'tcx> { + fn from(ty: Ty<'tcx>) -> Self { + Term::Ty(ty) + } +} + +impl<'tcx> From> for Term<'tcx> { + fn from(c: Const<'tcx>) -> Self { + Term::Const(c) + } +} + +impl<'tcx> Term<'tcx> { + pub fn ty(&self) -> Option> { + if let Term::Ty(ty) = self { Some(*ty) } else { None } + } + pub fn ct(&self) -> Option> { + if let Term::Const(c) = self { Some(*c) } else { None } + } +} + /// This kind of predicate has no *direct* correspondent in the /// syntax, but it roughly corresponds to the syntactic forms: /// @@ -808,7 +860,7 @@ pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; #[derive(HashStable, TypeFoldable)] pub struct ProjectionPredicate<'tcx> { pub projection_ty: ProjectionTy<'tcx>, - pub ty: Ty<'tcx>, + pub term: Term<'tcx>, } pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>; @@ -833,8 +885,8 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { self.map_bound(|predicate| predicate.projection_ty.trait_ref(tcx)) } - pub fn ty(&self) -> Binder<'tcx, Ty<'tcx>> { - self.map_bound(|predicate| predicate.ty) + pub fn term(&self) -> Binder<'tcx, Term<'tcx>> { + self.map_bound(|predicate| predicate.term) } /// The `DefId` of the `TraitItem` for the associated type. @@ -1321,6 +1373,11 @@ impl<'tcx> ParamEnv<'tcx> { self.packed.tag().constness } + #[inline] + pub fn is_const(self) -> bool { + self.packed.tag().constness == hir::Constness::Const + } + /// Construct a trait environment with no where-clauses in scope /// where the values of all `impl Trait` and other hidden types /// are revealed. This is suitable for monomorphized, post-typeck @@ -1415,7 +1472,7 @@ impl<'tcx> ParamEnv<'tcx> { Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, Reveal::All => { - if value.is_known_global() { + if value.is_global() { ParamEnvAnd { param_env: self.without_caller_bounds(), value } } else { ParamEnvAnd { param_env: self, value } @@ -1436,6 +1493,7 @@ impl<'tcx> PolyTraitRef<'tcx> { polarity: ty::ImplPolarity::Positive, }) } + #[inline] pub fn without_const(self) -> PolyTraitPredicate<'tcx> { self.with_constness(BoundConstness::NotConst) @@ -1502,8 +1560,7 @@ pub struct VariantDef { /// If this variant is a struct variant, then this is `None`. pub ctor_def_id: Option, /// Variant or struct name. - #[stable_hasher(project(name))] - pub ident: Ident, + pub name: Symbol, /// Discriminant of this variant. pub discr: VariantDiscr, /// Fields of this variant. @@ -1532,7 +1589,7 @@ impl VariantDef { /// If someone speeds up attribute loading to not be a performance concern, they can /// remove this hack and use the constructor `DefId` everywhere. pub fn new( - ident: Ident, + name: Symbol, variant_did: Option, ctor_def_id: Option, discr: VariantDiscr, @@ -1544,9 +1601,9 @@ impl VariantDef { is_field_list_non_exhaustive: bool, ) -> Self { debug!( - "VariantDef::new(ident = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, + "VariantDef::new(name = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, fields = {:?}, ctor_kind = {:?}, adt_kind = {:?}, parent_did = {:?})", - ident, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, + name, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, ); let mut flags = VariantFlags::NO_VARIANT_FLAGS; @@ -1561,7 +1618,7 @@ impl VariantDef { VariantDef { def_id: variant_did.unwrap_or(parent_did), ctor_def_id, - ident, + name, discr, fields, ctor_kind, @@ -1580,6 +1637,11 @@ impl VariantDef { pub fn is_recovered(&self) -> bool { self.flags.intersects(VariantFlags::IS_RECOVERED) } + + /// Computes the `Ident` of this variant by looking up the `Span` + pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident { + Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap()) + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] @@ -1598,8 +1660,7 @@ pub enum VariantDiscr { #[derive(Debug, HashStable, TyEncodable, TyDecodable)] pub struct FieldDef { pub did: DefId, - #[stable_hasher(project(name))] - pub ident: Ident, + pub name: Symbol, pub vis: Visibility, } @@ -1774,6 +1835,11 @@ impl<'tcx> FieldDef { pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { tcx.type_of(self.did).subst(tcx, subst) } + + /// Computes the `Ident` of this variant by looking up the `Span` + pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident { + Ident::new(self.name, tcx.def_ident_span(self.did).unwrap()) + } } pub type Attributes<'tcx> = &'tcx [ast::Attribute]; @@ -1890,7 +1956,10 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option { - variant.fields.iter().position(|field| self.hygienic_eq(ident, field.ident, variant.def_id)) + variant + .fields + .iter() + .position(|field| self.hygienic_eq(ident, field.ident(self), variant.def_id)) } /// Returns `true` if the impls are the same polarity and the trait either @@ -2069,8 +2138,7 @@ impl<'tcx> TyCtxt<'tcx> { /// with the name of the crate containing the impl. pub fn span_of_impl(self, impl_did: DefId) -> Result { if let Some(impl_did) = impl_did.as_local() { - let hir_id = self.hir().local_def_id_to_hir_id(impl_did); - Ok(self.hir().span(hir_id)) + Ok(self.def_span(impl_did)) } else { Err(self.crate_name(impl_did.krate)) } @@ -2117,7 +2185,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Yields the parent function's `LocalDefId` if `def_id` is an `impl Trait` definition. pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { let def_id = def_id.as_local()?; - if let Node::Item(item) = tcx.hir().get(tcx.hir().local_def_id_to_hir_id(def_id)) { + if let Node::Item(item) = tcx.hir().get_by_def_id(def_id) { if let hir::ItemKind::OpaqueTy(ref opaque_ty) = item.kind { return match opaque_ty.origin { hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => { diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 84ab42a760..808be446b2 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -34,8 +34,8 @@ impl<'tcx> TyCtxt<'tcx> { /// Erase the regions in `value` and then fully normalize all the /// types found within. The result will also have regions erased. /// - /// This is appropriate to use only after type-check: it assumes - /// that normalization will succeed, for example. + /// This should only be used outside of type inference. For example, + /// it assumes that normalization will succeed. pub fn normalize_erasing_regions(self, param_env: ty::ParamEnv<'tcx>, value: T) -> T where T: TypeFoldable<'tcx>, @@ -192,7 +192,7 @@ impl<'tcx> TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> { self.normalize_generic_arg_after_erasing_regions(ty.into()).expect_ty() } - fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { self.normalize_generic_arg_after_erasing_regions(c.into()).expect_const() } @@ -244,13 +244,10 @@ impl<'tcx> FallibleTypeFolder<'tcx> for TryNormalizeAfterErasingRegionsFolder<'t } } - fn try_fold_const( - &mut self, - c: &'tcx ty::Const<'tcx>, - ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { + fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result, Self::Error> { match self.try_normalize_generic_arg_after_erasing_regions(c.into()) { Ok(t) => Ok(t.expect_const()), - Err(_) => Err(NormalizationError::Const(*c)), + Err(_) => Err(NormalizationError::Const(c)), } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 94127a144d..94cea505c3 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -66,7 +66,7 @@ pub trait Printer<'tcx>: Sized { predicates: &'tcx ty::List>>, ) -> Result; - fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result; + fn print_const(self, ct: ty::Const<'tcx>) -> Result; fn path_crate(self, cnum: CrateNum) -> Result; @@ -321,19 +321,11 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { characteristic_def_id_of_type_cached(ty, &mut SsoHashSet::new()) } -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind { - type Output = P::Region; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.print_region(self) - } -} - impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'_> { type Output = P::Region; type Error = P::Error; fn print(&self, cx: P) -> Result { - cx.print_region(self) + cx.print_region(*self) } } @@ -341,7 +333,7 @@ impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { type Output = P::Type; type Error = P::Error; fn print(&self, cx: P) -> Result { - cx.print_type(self) + cx.print_type(*self) } } @@ -355,10 +347,10 @@ impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> } } -impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::Const<'tcx> { +impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> { type Output = P::Const; type Error = P::Error; fn print(&self, cx: P) -> Result { - cx.print_const(self) + cx.print_const(*self) } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 47a9234419..bf7370c9e0 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,8 +1,9 @@ use crate::mir::interpret::{AllocRange, ConstValue, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; -use crate::ty::{self, ConstInt, DefIdTree, ParamConst, ScalarInt, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, ConstInt, DefIdTree, ParamConst, ScalarInt, Term, Ty, TyCtxt, TypeFoldable}; use rustc_apfloat::ieee::{Double, Single}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::intern::Interned; use rustc_data_structures::sso::SsoHashSet; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; @@ -130,11 +131,13 @@ pub fn with_no_visible_paths R, R>(f: F) -> R { /// /// Regions not selected by the region highlight mode are presently /// unaffected. -#[derive(Copy, Clone, Default)] -pub struct RegionHighlightMode { +#[derive(Copy, Clone)] +pub struct RegionHighlightMode<'tcx> { + tcx: TyCtxt<'tcx>, + /// If enabled, when we see the selected region, use "`'N`" /// instead of the ordinary behavior. - highlight_regions: [Option<(ty::RegionKind, usize)>; 3], + highlight_regions: [Option<(ty::Region<'tcx>, usize)>; 3], /// If enabled, when printing a "free region" that originated from /// the given `ty::BoundRegionKind`, print it as "`'1`". Free regions that would ordinarily @@ -146,12 +149,20 @@ pub struct RegionHighlightMode { highlight_bound_region: Option<(ty::BoundRegionKind, usize)>, } -impl RegionHighlightMode { +impl<'tcx> RegionHighlightMode<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { + tcx, + highlight_regions: Default::default(), + highlight_bound_region: Default::default(), + } + } + /// If `region` and `number` are both `Some`, invokes /// `highlighting_region`. pub fn maybe_highlighting_region( &mut self, - region: Option>, + region: Option>, number: Option, ) { if let Some(k) = region { @@ -162,24 +173,24 @@ impl RegionHighlightMode { } /// Highlights the region inference variable `vid` as `'N`. - pub fn highlighting_region(&mut self, region: ty::Region<'_>, number: usize) { + pub fn highlighting_region(&mut self, region: ty::Region<'tcx>, number: usize) { let num_slots = self.highlight_regions.len(); let first_avail_slot = self.highlight_regions.iter_mut().find(|s| s.is_none()).unwrap_or_else(|| { bug!("can only highlight {} placeholders at a time", num_slots,) }); - *first_avail_slot = Some((*region, number)); + *first_avail_slot = Some((region, number)); } /// Convenience wrapper for `highlighting_region`. pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) { - self.highlighting_region(&ty::ReVar(vid), number) + self.highlighting_region(self.tcx.mk_region(ty::ReVar(vid)), number) } /// Returns `Some(n)` with the number to use for the given region, if any. fn region_highlighted(&self, region: ty::Region<'_>) -> Option { self.highlight_regions.iter().find_map(|h| match h { - Some((r, n)) if r == region => Some(*n), + Some((r, n)) if *r == region => Some(*n), _ => None, }) } @@ -458,7 +469,7 @@ pub trait PrettyPrinter<'tcx>: // that's public and whose identifier isn't `_`. let reexport = self .tcx() - .item_children(visible_parent) + .module_children(visible_parent) .iter() .filter(|child| child.res.opt_def_id() == Some(def_id)) .find(|child| child.vis.is_public() && child.ident.name != kw::Underscore) @@ -595,7 +606,7 @@ pub trait PrettyPrinter<'tcx>: 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) { + if let Some(name) = self.ty_infer_name(ty_vid) { p!(write("{}", name)) } else { if verbose { @@ -671,8 +682,7 @@ pub trait PrettyPrinter<'tcx>: p!("generator"); // FIXME(eddyb) should use `def_span`. if let Some(did) = did.as_local() { - let hir_id = self.tcx().hir().local_def_id_to_hir_id(did); - let span = self.tcx().hir().span(hir_id); + let span = self.tcx().def_span(did); p!(write( "@{}", // This may end up in stderr diagnostics but it may also be emitted @@ -708,11 +718,10 @@ pub trait PrettyPrinter<'tcx>: p!(write("closure")); // FIXME(eddyb) should use `def_span`. if let Some(did) = did.as_local() { - let hir_id = self.tcx().hir().local_def_id_to_hir_id(did); if self.tcx().sess.opts.debugging_opts.span_free_formats { p!("@", print_def_path(did.to_def_id(), substs)); } else { - let span = self.tcx().hir().span(hir_id); + let span = self.tcx().def_span(did); p!(write( "@{}", // This may end up in stderr diagnostics but it may also be emitted @@ -745,14 +754,14 @@ pub trait PrettyPrinter<'tcx>: p!("[", print(ty), "; "); if self.tcx().sess.verbose() { p!(write("{:?}", sz)); - } else if let ty::ConstKind::Unevaluated(..) = sz.val { + } else if let ty::ConstKind::Unevaluated(..) = sz.val() { // Do not try to evaluate unevaluated constants. If we are const evaluating an // array length anon const, rustc will (with debug assertions) print the // constant's path. Which will end up here again. p!("_"); - } else if let Some(n) = sz.val.try_to_bits(self.tcx().data_layout.pointer_size) { + } else if let Some(n) = sz.val().try_to_bits(self.tcx().data_layout.pointer_size) { p!(write("{}", n)); - } else if let ty::ConstKind::Param(param) = sz.val { + } else if let ty::ConstKind::Param(param) = sz.val() { p!(write("{}", param)); } else { p!("_"); @@ -801,7 +810,7 @@ pub trait PrettyPrinter<'tcx>: let trait_ref = proj_ref.required_poly_trait_ref(self.tcx()); // Projection type entry -- the def-id for naming, and the ty. - let proj_ty = (proj_ref.projection_def_id(), proj_ref.ty()); + let proj_ty = (proj_ref.projection_def_id(), proj_ref.term()); self.insert_trait_and_projection( trait_ref, @@ -852,8 +861,10 @@ pub trait PrettyPrinter<'tcx>: } p!(")"); - if !return_ty.skip_binder().is_unit() { - p!("-> ", print(return_ty)); + if let Term::Ty(ty) = return_ty.skip_binder() { + if !ty.is_unit() { + p!("-> ", print(return_ty)); + } } p!(write("{}", if paren_needed { ")" } else { "" })); @@ -904,23 +915,28 @@ pub trait PrettyPrinter<'tcx>: first = false; } - for (assoc_item_def_id, ty) in assoc_items { + for (assoc_item_def_id, term) in assoc_items { if !first { p!(", "); } - p!(write("{} = ", self.tcx().associated_item(assoc_item_def_id).ident)); - - // Skip printing `<[generator@] as Generator<_>>::Return` from async blocks - match ty.skip_binder().kind() { - ty::Projection(ty::ProjectionTy { item_def_id, .. }) - if Some(*item_def_id) == self.tcx().lang_items().generator_return() => - { - p!("[async output]") + p!(write("{} = ", self.tcx().associated_item(assoc_item_def_id).name)); + + match term.skip_binder() { + Term::Ty(ty) => { + // Skip printing `<[generator@] as Generator<_>>::Return` from async blocks + if matches!( + ty.kind(), ty::Projection(ty::ProjectionTy { item_def_id, .. }) + if Some(*item_def_id) == self.tcx().lang_items().generator_return() + ) { + p!("[async output]") + } else { + p!(print(ty)) + } } - _ => { - p!(print(ty)) + Term::Const(c) => { + p!(print(c)); } - } + }; first = false; } @@ -945,8 +961,11 @@ pub trait PrettyPrinter<'tcx>: fn insert_trait_and_projection( &mut self, trait_ref: ty::PolyTraitRef<'tcx>, - proj_ty: Option<(DefId, ty::Binder<'tcx, Ty<'tcx>>)>, - traits: &mut BTreeMap, BTreeMap>>>, + proj_ty: Option<(DefId, ty::Binder<'tcx, Term<'tcx>>)>, + traits: &mut BTreeMap< + ty::PolyTraitRef<'tcx>, + BTreeMap>>, + >, fn_traits: &mut BTreeMap, OpaqueFnEntry<'tcx>>, ) { let trait_def_id = trait_ref.def_id(); @@ -996,7 +1015,11 @@ pub trait PrettyPrinter<'tcx>: } } - fn infer_ty_name(&self, _: ty::TyVid) -> Option { + fn ty_infer_name(&self, _: ty::TyVid) -> Option { + None + } + + fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option { None } @@ -1021,7 +1044,11 @@ pub trait PrettyPrinter<'tcx>: let mut projections = predicates.projection_bounds(); if let (Some(proj), None) = (projections.next(), projections.next()) { let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); - p!(pretty_fn_sig(&tys, false, proj.skip_binder().ty)); + p!(pretty_fn_sig( + &tys, + false, + proj.skip_binder().term.ty().expect("Return type was a const") + )); resugared = true; } } @@ -1041,7 +1068,7 @@ pub trait PrettyPrinter<'tcx>: // Don't print `'_` if there's no unerased regions. let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, + GenericArgKind::Lifetime(r) => !r.is_erased(), _ => false, }); let mut args = args.iter().cloned().filter(|arg| match arg.unpack() { @@ -1125,13 +1152,13 @@ pub trait PrettyPrinter<'tcx>: fn pretty_print_const( mut self, - ct: &'tcx ty::Const<'tcx>, + ct: ty::Const<'tcx>, print_ty: bool, ) -> Result { define_scoped_cx!(self); if self.tcx().sess.verbose() { - p!(write("Const({:?}: {:?})", ct.val, ct.ty)); + p!(write("Const({:?}: {:?})", ct.val(), ct.ty())); return Ok(self); } @@ -1143,7 +1170,7 @@ pub trait PrettyPrinter<'tcx>: write!(this, "_")?; Ok(this) }, - |this| this.print_type(ct.ty), + |this| this.print_type(ct.ty()), ": ", )?; } else { @@ -1152,37 +1179,45 @@ pub trait PrettyPrinter<'tcx>: }}; } - match ct.val { - ty::ConstKind::Unevaluated(uv) => { - if let Some(promoted) = uv.promoted { - let substs = uv.substs_.unwrap(); - p!(print_value_path(uv.def.did, substs)); - p!(write("::{:?}", promoted)); - } else { - let tcx = self.tcx(); - match tcx.def_kind(uv.def.did) { - DefKind::Static | DefKind::Const | DefKind::AssocConst => { - p!(print_value_path(uv.def.did, uv.substs(tcx))) - } - _ => { - if uv.def.is_local() { - let span = tcx.def_span(uv.def.did); - if let Ok(snip) = tcx.sess.source_map().span_to_snippet(span) { - p!(write("{}", snip)) - } else { - print_underscore!() - } + match ct.val() { + ty::ConstKind::Unevaluated(ty::Unevaluated { + def, + substs, + promoted: Some(promoted), + }) => { + p!(print_value_path(def.did, substs)); + p!(write("::{:?}", promoted)); + } + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: None }) => { + match self.tcx().def_kind(def.did) { + DefKind::Static | DefKind::Const | DefKind::AssocConst => { + p!(print_value_path(def.did, substs)) + } + _ => { + if def.is_local() { + let span = self.tcx().def_span(def.did); + if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) { + p!(write("{}", snip)) } else { print_underscore!() } + } else { + print_underscore!() } } } } - ty::ConstKind::Infer(..) => print_underscore!(), + ty::ConstKind::Infer(infer_ct) => { + match infer_ct { + ty::InferConst::Var(ct_vid) + if let Some(name) = self.const_infer_name(ct_vid) => + p!(write("{}", name)), + _ => print_underscore!(), + } + } ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), ty::ConstKind::Value(value) => { - return self.pretty_print_const_value(value, ct.ty, print_ty); + return self.pretty_print_const_value(value, ct.ty(), print_ty); } ty::ConstKind::Bound(debruijn, bound_var) => { @@ -1219,16 +1254,23 @@ pub trait PrettyPrinter<'tcx>: // Byte strings (&[u8; N]) ty::Ref( _, - ty::TyS { - kind: - ty::Array( - ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. }, - ty::Const { - val: ty::ConstKind::Value(ConstValue::Scalar(int)), .. - }, - ), - .. - }, + Ty(Interned( + ty::TyS { + kind: + ty::Array( + Ty(Interned(ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. }, _)), + ty::Const(Interned( + ty::ConstS { + val: ty::ConstKind::Value(ConstValue::Scalar(int)), + .. + }, + _, + )), + ), + .. + }, + _, + )), _, ) => match self.tcx().get_global_alloc(alloc_id) { Some(GlobalAlloc::Memory(alloc)) => { @@ -1386,7 +1428,7 @@ pub trait PrettyPrinter<'tcx>: // Byte/string slices, printed as (byte) string literals. ( ConstValue::Slice { data, start, end }, - ty::Ref(_, ty::TyS { kind: ty::Slice(t), .. }, _), + ty::Ref(_, Ty(Interned(ty::TyS { kind: ty::Slice(t), .. }, _)), _), ) if *t == u8_type => { // The `inspect` here is okay since we checked the bounds, and there are // no relocations (we have an active slice reference here). We don't use @@ -1396,7 +1438,7 @@ pub trait PrettyPrinter<'tcx>: } ( ConstValue::Slice { data, start, end }, - ty::Ref(_, ty::TyS { kind: ty::Str, .. }, _), + ty::Ref(_, Ty(Interned(ty::TyS { kind: ty::Str, .. }, _)), _), ) => { // The `inspect` here is okay since we checked the bounds, and there are no // relocations (we have an active `str` reference here). We don't use this @@ -1407,7 +1449,7 @@ pub trait PrettyPrinter<'tcx>: Ok(self) } (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => { - let n = n.val.try_to_bits(self.tcx().data_layout.pointer_size).unwrap(); + let n = n.val().try_to_bits(self.tcx().data_layout.pointer_size).unwrap(); // cast is ok because we already checked for pointer size (32 or 64 bit) above let range = AllocRange { start: offset, size: Size::from_bytes(n) }; @@ -1419,7 +1461,7 @@ pub trait PrettyPrinter<'tcx>: // Aggregates, printed as array/tuple/struct/variant construction syntax. // - // NB: the `potentially_has_param_types_or_consts` check ensures that we can use + // NB: the `has_param_types_or_consts` check ensures that we can use // the `destructure_const` query with an empty `ty::ParamEnv` without // introducing ICEs (e.g. via `layout_of`) from missing bounds. // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` @@ -1427,13 +1469,19 @@ pub trait PrettyPrinter<'tcx>: // // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the // correct `ty::ParamEnv` to allow printing *all* constant values. - (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) - if !ty.potentially_has_param_types_or_consts() => - { - let contents = self.tcx().destructure_const( + (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => { + let Some(contents) = self.tcx().try_destructure_const( ty::ParamEnv::reveal_all() - .and(self.tcx().mk_const(ty::Const { val: ty::ConstKind::Value(ct), ty })), - ); + .and(self.tcx().mk_const(ty::ConstS { val: ty::ConstKind::Value(ct), ty })), + ) else { + // Fall back to debug pretty printing for invalid constants. + p!(write("{:?}", ct)); + if print_ty { + p!(": ", print(ty)); + } + return Ok(self); + }; + let fields = contents.fields.iter().copied(); match *ty.kind() { @@ -1475,7 +1523,7 @@ pub trait PrettyPrinter<'tcx>: if !first { p!(", "); } - p!(write("{}: ", field_def.ident), print(field)); + p!(write("{}: ", field_def.name), print(field)); first = false; } p!(" }}"); @@ -1520,9 +1568,10 @@ pub struct FmtPrinterData<'a, 'tcx, F> { binder_depth: usize, printed_type_count: usize, - pub region_highlight_mode: RegionHighlightMode, + pub region_highlight_mode: RegionHighlightMode<'tcx>, - pub name_resolver: Option Option>>, + pub ty_infer_name_resolver: Option Option + 'a>>, + pub const_infer_name_resolver: Option) -> Option + 'a>>, } impl<'a, 'tcx, F> Deref for FmtPrinter<'a, 'tcx, F> { @@ -1550,8 +1599,9 @@ impl<'a, 'tcx, F> FmtPrinter<'a, 'tcx, F> { region_index: 0, binder_depth: 0, printed_type_count: 0, - region_highlight_mode: RegionHighlightMode::default(), - name_resolver: None, + region_highlight_mode: RegionHighlightMode::new(tcx), + ty_infer_name_resolver: None, + const_infer_name_resolver: None, })) } } @@ -1691,7 +1741,7 @@ impl<'tcx, F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { self.pretty_print_dyn_existential(predicates) } - fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result { + fn print_const(self, ct: ty::Const<'tcx>) -> Result { self.pretty_print_const(ct, true) } @@ -1784,10 +1834,11 @@ impl<'tcx, F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { self = print_prefix(self)?; // Don't print `'_` if there's no unerased regions. - let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, - _ => false, - }); + let print_regions = self.tcx.sess.verbose() + || args.iter().any(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => !r.is_erased(), + _ => false, + }); let args = args.iter().cloned().filter(|arg| match arg.unpack() { GenericArgKind::Lifetime(_) => print_regions, _ => true, @@ -1805,8 +1856,12 @@ impl<'tcx, F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { } impl<'tcx, F: fmt::Write> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { - fn infer_ty_name(&self, id: ty::TyVid) -> Option { - self.0.name_resolver.as_ref().and_then(|func| func(id)) + fn ty_infer_name(&self, id: ty::TyVid) -> Option { + self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id)) + } + + fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option { + self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id)) } fn print_value_path( @@ -2044,7 +2099,7 @@ impl<'a, 'tcx> ty::TypeFolder<'tcx> for RegionFolder<'a, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { let name = &mut self.name; let region = match *r { - ty::ReLateBound(_, br) => self.region_map.entry(br).or_insert_with(|| name(br)), + ty::ReLateBound(_, br) => *self.region_map.entry(br).or_insert_with(|| name(br)), ty::RePlaceholder(ty::PlaceholderRegion { name: kind, .. }) => { // If this is an anonymous placeholder, don't rename. Otherwise, in some // async fns, we get a `for<'r> Send` bound @@ -2053,7 +2108,7 @@ impl<'a, 'tcx> ty::TypeFolder<'tcx> for RegionFolder<'a, 'tcx> { _ => { // Index doesn't matter, since this is just for naming and these never get bound let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind }; - self.region_map.entry(br).or_insert_with(|| name(br)) + *self.region_map.entry(br).or_insert_with(|| name(br)) } } } @@ -2246,7 +2301,6 @@ impl<'tcx, F: fmt::Write> FmtPrinter<'_, 'tcx, F> { T: TypeFoldable<'tcx>, { struct LateBoundRegionNameCollector<'a, 'tcx> { - tcx: TyCtxt<'tcx>, used_region_names: &'a mut FxHashSet, type_collector: SsoHashSet>, } @@ -2254,13 +2308,9 @@ impl<'tcx, F: fmt::Write> FmtPrinter<'_, 'tcx, F> { impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_, 'tcx> { type BreakTy = (); - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - #[instrument(skip(self), level = "trace")] fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - trace!("address: {:p}", r); + trace!("address: {:p}", r.0.0); if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) = *r { self.used_region_names.insert(name); } else if let ty::RePlaceholder(ty::PlaceholderRegion { @@ -2288,7 +2338,6 @@ impl<'tcx, F: fmt::Write> FmtPrinter<'_, 'tcx, F> { self.used_region_names.clear(); let mut collector = LateBoundRegionNameCollector { - tcx: self.tcx, used_region_names: &mut self.used_region_names, type_collector: SsoHashSet::new(), }; @@ -2358,7 +2407,7 @@ macro_rules! define_print_and_forward_display { } // HACK(eddyb) this is separate because `ty::RegionKind` doesn't need lifting. -impl fmt::Display for ty::RegionKind { +impl<'tcx> fmt::Display for ty::Region<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { self.print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; @@ -2407,10 +2456,33 @@ impl<'tcx> ty::Binder<'tcx, ty::TraitRef<'tcx>> { } } +#[derive(Copy, Clone, TypeFoldable, Lift)] +pub struct TraitPredPrintModifiersAndPath<'tcx>(ty::TraitPredicate<'tcx>); + +impl<'tcx> fmt::Debug for TraitPredPrintModifiersAndPath<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl<'tcx> ty::TraitPredicate<'tcx> { + pub fn print_modifiers_and_trait_path(self) -> TraitPredPrintModifiersAndPath<'tcx> { + TraitPredPrintModifiersAndPath(self) + } +} + +impl<'tcx> ty::PolyTraitPredicate<'tcx> { + pub fn print_modifiers_and_trait_path( + self, + ) -> ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>> { + self.map_bound(TraitPredPrintModifiersAndPath) + } +} + forward_display_to_print! { Ty<'tcx>, &'tcx ty::List>>, - &'tcx ty::Const<'tcx>, + ty::Const<'tcx>, // HACK(eddyb) these are exhaustive instead of generic, // because `for<'tcx>` isn't possible yet. @@ -2421,6 +2493,7 @@ forward_display_to_print! { ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>, ty::Binder<'tcx, ty::FnSig<'tcx>>, ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>>, ty::Binder<'tcx, ty::SubtypePredicate<'tcx>>, ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>, ty::Binder<'tcx, ty::OutlivesPredicate, ty::Region<'tcx>>>, @@ -2449,8 +2522,8 @@ define_print_and_forward_display! { } ty::ExistentialProjection<'tcx> { - let name = cx.tcx().associated_item(self.item_def_id).ident; - p!(write("{} = ", name), print(self.ty)) + let name = cx.tcx().associated_item(self.item_def_id).name; + p!(write("{} = ", name), print(self.term)) } ty::ExistentialPredicate<'tcx> { @@ -2485,6 +2558,18 @@ define_print_and_forward_display! { p!(print_def_path(self.0.def_id, &[])); } + TraitPredPrintModifiersAndPath<'tcx> { + if let ty::BoundConstness::ConstIfConst = self.0.constness { + p!("~const ") + } + + if let ty::ImplPolarity::Negative = self.0.polarity { + p!("!") + } + + p!(print(self.0.trait_ref.print_only_trait_path())); + } + ty::ParamTy { p!(write("{}", self.name)) } @@ -2502,12 +2587,22 @@ define_print_and_forward_display! { } ty::TraitPredicate<'tcx> { - p!(print(self.trait_ref.self_ty()), ": ", - print(self.trait_ref.print_only_trait_path())) + p!(print(self.trait_ref.self_ty()), ": "); + if let ty::BoundConstness::ConstIfConst = self.constness { + p!("~const "); + } + p!(print(self.trait_ref.print_only_trait_path())) } ty::ProjectionPredicate<'tcx> { - p!(print(self.projection_ty), " == ", print(self.ty)) + p!(print(self.projection_ty), " == ", print(self.term)) + } + + ty::Term<'tcx> { + match self { + ty::Term::Ty(ty) => p!(print(ty)), + ty::Term::Const(c) => p!(print(c)), + } } ty::ProjectionTy<'tcx> { @@ -2547,7 +2642,7 @@ define_print_and_forward_display! { write("` implements the trait `{}`", kind)) } ty::PredicateKind::ConstEvaluatable(uv) => { - p!("the constant `", print_value_path(uv.def.did, uv.substs_.map_or(&[], |x| x)), "` can be evaluated") + p!("the constant `", print_value_path(uv.def.did, uv.substs), "` can be evaluated") } ty::PredicateKind::ConstEquate(c1, c2) => { p!("the constant `", print(c1), "` equals `", print(c2), "`") @@ -2602,7 +2697,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N // Iterate external crate defs but be mindful about visibility while let Some(def) = queue.pop() { - for child in tcx.item_children(def).iter() { + for child in tcx.module_children(def).iter() { if !child.vis.is_public() { continue; } @@ -2615,7 +2710,9 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N collect_fn(&child.ident, ns, def_id); } - if seen_defs.insert(def_id) { + if matches!(defkind, DefKind::Mod | DefKind::Enum | DefKind::Trait) + && seen_defs.insert(def_id) + { queue.push(def_id); } } @@ -2715,5 +2812,5 @@ pub struct OpaqueFnEntry<'tcx> { has_fn_once: bool, fn_mut_trait_ref: Option>, fn_trait_ref: Option>, - return_ty: Option>>, + return_ty: Option>>, } diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 3af1b3a044..1688e59cdd 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -1,7 +1,7 @@ use crate::dep_graph; -use crate::hir::exports::Export; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintLevelMap; +use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use crate::middle::lib_features::LibFeatures; @@ -56,7 +56,6 @@ use rustc_ast as ast; use rustc_attr as attr; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; -use std::collections::BTreeMap; use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; @@ -107,6 +106,12 @@ impl<'tcx> TyCtxt<'tcx> { #[inline(always)] fn noop(_: &T) {} +/// Helper to ensure that queries only return `Copy` types. +#[inline(always)] +fn copy(x: &T) -> T { + *x +} + macro_rules! query_helper_param_ty { (DefId) => { impl IntoQueryParam }; ($K:ty) => { $K }; @@ -244,7 +249,7 @@ macro_rules! define_callbacks { let key = key.into_query_param(); opt_remap_env_constness!([$($modifiers)*][key]); - let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, Clone::clone); + let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, copy); let lookup = match cached { Ok(value) => return value, @@ -348,6 +353,13 @@ mod sealed { } } + impl<'a, P: Copy> IntoQueryParam

Stream for Pin

+#[unstable(feature = "async_iterator", issue = "79024")] +impl

AsyncIterator for Pin

where P: DerefMut, - P::Target: Stream, + P::Target: AsyncIterator, { - type Item = ::Item; + type Item = ::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - ::poll_next(self.as_deref_mut(), cx) + ::poll_next(self.as_deref_mut(), cx) } fn size_hint(&self) -> (usize, Option) { diff --git a/library/core/src/stream/from_iter.rs b/library/core/src/async_iter/from_iter.rs similarity index 54% rename from library/core/src/stream/from_iter.rs rename to library/core/src/async_iter/from_iter.rs index eb9a0fd284..3180187afc 100644 --- a/library/core/src/stream/from_iter.rs +++ b/library/core/src/async_iter/from_iter.rs @@ -1,31 +1,31 @@ use crate::pin::Pin; -use crate::stream::Stream; +use crate::async_iter::AsyncIterator; use crate::task::{Context, Poll}; -/// A stream that was created from iterator. +/// An async iterator that was created from iterator. /// -/// This stream is created by the [`from_iter`] function. +/// This async iterator is created by the [`from_iter`] function. /// See it documentation for more. /// /// [`from_iter`]: fn.from_iter.html -#[unstable(feature = "stream_from_iter", issue = "81798")] +#[unstable(feature = "async_iter_from_iter", issue = "81798")] #[derive(Clone, Debug)] pub struct FromIter { iter: I, } -#[unstable(feature = "stream_from_iter", issue = "81798")] +#[unstable(feature = "async_iter_from_iter", issue = "81798")] impl Unpin for FromIter {} -/// Converts an iterator into a stream. -#[unstable(feature = "stream_from_iter", issue = "81798")] +/// Converts an iterator into an async iterator. +#[unstable(feature = "async_iter_from_iter", issue = "81798")] pub fn from_iter(iter: I) -> FromIter { FromIter { iter: iter.into_iter() } } -#[unstable(feature = "stream_from_iter", issue = "81798")] -impl Stream for FromIter { +#[unstable(feature = "async_iter_from_iter", issue = "81798")] +impl AsyncIterator for FromIter { type Item = I::Item; fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { diff --git a/library/core/src/stream/mod.rs b/library/core/src/async_iter/mod.rs similarity index 56% rename from library/core/src/stream/mod.rs rename to library/core/src/async_iter/mod.rs index b59a46d5f3..0c6f637711 100644 --- a/library/core/src/stream/mod.rs +++ b/library/core/src/async_iter/mod.rs @@ -1,10 +1,9 @@ //! 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, +//! 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. +//! you'll quickly run into 'async iterators'. Async Iterators 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: //! @@ -12,71 +11,71 @@ //! //! This module is largely organized by type: //! -//! * [Traits] are the core portion: these traits define what kind of streams +//! * [Traits] are the core portion: these traits define what kind of async iterators //! 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. +//! * Functions provide some helpful ways to create some basic async iterators. //! * 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)'. +//! see '[Implementing Async Iterator](#implementing-async-iterator)'. //! //! [Traits]: #traits //! -//! That's it! Let's dig into streams. +//! That's it! Let's dig into async iterators. //! -//! # Stream +//! # Async Iterators //! -//! The heart and soul of this module is the [`Stream`] trait. The core of -//! [`Stream`] looks like this: +//! The heart and soul of this module is the [`AsyncIterator`] trait. The core of +//! [`AsyncIterator`] looks like this: //! //! ``` //! # use core::task::{Context, Poll}; //! # use core::pin::Pin; -//! trait Stream { +//! trait AsyncIterator { //! 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` +//! Unlike `Iterator`, `AsyncIterator` makes a distinction between the [`poll_next`] +//! method which is used when implementing an `AsyncIterator`, and a (to-be-implemented) +//! `next` method which is used when consuming an async iterator. Consumers of `AsyncIterator` //! only need to consider `next`, which when called, returns a future which -//! yields `Option`. +//! 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. +//! resolve, the future will wait until the async iterator is ready to yield again. //! -//! Individual streams may choose to resume iteration, and so calling `next` +//! Individual async iterators 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, +//! [`AsyncIterator`]'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 +//! [`poll_next`]: AsyncIterator::poll_next //! -//! # Implementing Stream +//! # Implementing Async Iterator //! -//! Creating a stream of your own involves two steps: creating a `struct` to -//! hold the stream's state, and then implementing [`Stream`] for that +//! Creating an async iterator of your own involves two steps: creating a `struct` to +//! hold the async iterator's state, and then implementing [`AsyncIterator`] for that //! `struct`. //! -//! Let's make a stream named `Counter` which counts from `1` to `5`: +//! Let's make an async iterator named `Counter` which counts from `1` to `5`: //! //! ```no_run -//! #![feature(async_stream)] -//! # use core::stream::Stream; +//! #![feature(async_iterator)] +//! # use core::async_iter::AsyncIterator; //! # use core::task::{Context, Poll}; //! # use core::pin::Pin; //! //! // First, the struct: //! -//! /// A stream which counts from one to five +//! /// An async iterator which counts from one to five //! struct Counter { //! count: usize, //! } @@ -90,9 +89,9 @@ //! } //! } //! -//! // Then, we implement `Stream` for our `Counter`: +//! // Then, we implement `AsyncIterator` for our `Counter`: //! -//! impl Stream for Counter { +//! impl AsyncIterator for Counter { //! // we will be counting with usize //! type Item = usize; //! @@ -113,17 +112,17 @@ //! //! # Laziness //! -//! Streams are *lazy*. This means that just creating a stream doesn't _do_ a -//! whole lot. Nothing really happens until you call `poll_next`. This is -//! sometimes a source of confusion when creating a stream solely for its side +//! Async iterators are *lazy*. This means that just creating an async iterator doesn't +//! _do_ a whole lot. Nothing really happens until you call `poll_next`. This is +//! sometimes a source of confusion when creating an async iterator 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 +//! warning: unused result that must be used: async iterators do nothing unless polled //! ``` +mod async_iter; mod from_iter; -mod stream; +pub use async_iter::AsyncIterator; pub use from_iter::{from_iter, FromIter}; -pub use stream::Stream; diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index bc3f7167fa..aef7ad7756 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -315,6 +315,7 @@ impl Ord for Cell { #[stable(feature = "cell_from", since = "1.12.0")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] impl const From for Cell { + /// Creates a new `Cell` containing the given value. fn from(t: T) -> Cell { Cell::new(t) } @@ -1244,6 +1245,7 @@ impl Ord for RefCell { #[stable(feature = "cell_from", since = "1.12.0")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] impl const From for RefCell { + /// Creates a new `RefCell` containing the given value. fn from(t: T) -> RefCell { RefCell::new(t) } @@ -1310,11 +1312,7 @@ impl Clone for BorrowRef<'_> { /// /// See the [module-level documentation](self) for more. #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr( - not(bootstrap), - must_not_suspend = "holding a Ref across suspend \ - points can cause BorrowErrors" -)] +#[must_not_suspend = "holding a Ref across suspend points can cause BorrowErrors"] pub struct Ref<'b, T: ?Sized + 'b> { value: &'b T, borrow: BorrowRef<'b>, @@ -1692,11 +1690,7 @@ impl<'b> BorrowRefMut<'b> { /// /// See the [module-level documentation](self) for more. #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr( - not(bootstrap), - must_not_suspend = "holding a RefMut across suspend \ - points can cause BorrowErrors" -)] +#[must_not_suspend = "holding a RefMut across suspend points can cause BorrowErrors"] pub struct RefMut<'b, T: ?Sized + 'b> { value: &'b mut T, borrow: BorrowRefMut<'b>, @@ -1967,6 +1961,7 @@ impl UnsafeCell { /// ``` #[inline(always)] #[stable(feature = "unsafe_cell_raw_get", since = "1.56.0")] + #[rustc_const_stable(feature = "unsafe_cell_raw_get", since = "1.56.0")] pub const fn raw_get(this: *const Self) -> *mut T { // We can just cast the pointer from `UnsafeCell` to `T` because of // #[repr(transparent)]. This exploits libstd's special status, there is @@ -1986,6 +1981,7 @@ impl Default for UnsafeCell { #[stable(feature = "cell_from", since = "1.12.0")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] impl const From for UnsafeCell { + /// Creates a new `UnsafeCell` containing the given value. fn from(t: T) -> UnsafeCell { UnsafeCell::new(t) } diff --git a/library/core/src/char/decode.rs b/library/core/src/char/decode.rs index 5dd8c5ef78..8b9f979b57 100644 --- a/library/core/src/char/decode.rs +++ b/library/core/src/char/decode.rs @@ -120,9 +120,34 @@ impl> Iterator for DecodeUtf16 { #[inline] fn size_hint(&self) -> (usize, Option) { let (low, high) = self.iter.size_hint(); - // we could be entirely valid surrogates (2 elements per - // char), or entirely non-surrogates (1 element per char) - (low / 2, high) + + let (low_buf, high_buf) = match self.buf { + // buf is empty, no additional elements from it. + None => (0, 0), + // `u` is a non surrogate, so it's always an additional character. + Some(u) if u < 0xD800 || 0xDFFF < u => (1, 1), + // `u` is a leading surrogate (it can never be a trailing surrogate and + // it's a surrogate due to the previous branch) and `self.iter` is empty. + // + // `u` can't be paired, since the `self.iter` is empty, + // so it will always become an additional element (error). + Some(_u) if high == Some(0) => (1, 1), + // `u` is a leading surrogate and `iter` may be non-empty. + // + // `u` can either pair with a trailing surrogate, in which case no additional elements + // are produced, or it can become an error, in which case it's an additional character (error). + Some(_u) => (0, 1), + }; + + // `self.iter` could contain entirely valid surrogates (2 elements per + // char), or entirely non-surrogates (1 element per char). + // + // On odd lower bound, at least one element must stay unpaired + // (with other elements from `self.iter`), so we round up. + let low = low.div_ceil(2) + low_buf; + let high = high.and_then(|h| h.checked_add(high_buf)); + + (low, high) } } diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 7250dca2ad..c4c0a5a6c7 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -9,14 +9,19 @@ use super::*; #[lang = "char"] impl char { - /// The highest valid code point a `char` can have. + /// The highest valid code point a `char` can have, `'\u{10FFFF}'`. /// - /// A `char` is a [Unicode Scalar Value], which means that it is a [Code - /// Point], but only ones within a certain range. `MAX` is the highest valid - /// code point that's a valid [Unicode Scalar Value]. + /// # Examples + /// + /// ``` + /// # fn something_which_returns_char() -> char { 'a' } + /// let c: char = something_which_returns_char(); + /// assert!(c <= char::MAX); /// - /// [Unicode Scalar Value]: https://www.unicode.org/glossary/#unicode_scalar_value - /// [Code Point]: https://www.unicode.org/glossary/#code_point + /// let value_at_max = char::MAX as u32; + /// assert_eq!(char::from_u32(value_at_max), Some('\u{10FFFF}')); + /// assert_eq!(char::from_u32(value_at_max + 1), None); + /// ``` #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const MAX: char = '\u{10ffff}'; diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index f65f84e93a..9364ac4f3e 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -89,14 +89,19 @@ const MAX_THREE_B: u32 = 0x10000; Cn Unassigned a reserved unassigned code point or a noncharacter */ -/// The highest valid code point a `char` can have. +/// The highest valid code point a `char` can have, `'\u{10FFFF}'`. /// -/// A [`char`] is a [Unicode Scalar Value], which means that it is a [Code -/// Point], but only ones within a certain range. `MAX` is the highest valid -/// code point that's a valid [Unicode Scalar Value]. +/// # Examples /// -/// [Unicode Scalar Value]: https://www.unicode.org/glossary/#unicode_scalar_value -/// [Code Point]: https://www.unicode.org/glossary/#code_point +/// ``` +/// # fn something_which_returns_char() -> char { 'a' } +/// let c: char = something_which_returns_char(); +/// assert!(c <= char::MAX); +/// +/// let value_at_max = char::MAX as u32; +/// assert_eq!(char::from_u32(value_at_max), Some('\u{10FFFF}')); +/// assert_eq!(char::from_u32(value_at_max + 1), None); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub const MAX: char = char::MAX; diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index deed9901cc..a3e7747991 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -199,9 +199,20 @@ use self::Ordering::*; #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "==")] #[doc(alias = "!=")] -#[rustc_on_unimplemented( - message = "can't compare `{Self}` with `{Rhs}`", - label = "no implementation for `{Self} == {Rhs}`" +#[cfg_attr( + bootstrap, + rustc_on_unimplemented( + message = "can't compare `{Self}` with `{Rhs}`", + label = "no implementation for `{Self} == {Rhs}`" + ) +)] +#[cfg_attr( + not(bootstrap), + rustc_on_unimplemented( + message = "can't compare `{Self}` with `{Rhs}`", + label = "no implementation for `{Self} == {Rhs}`", + append_const_msg, + ) )] #[rustc_diagnostic_item = "PartialEq"] pub trait PartialEq { @@ -661,20 +672,37 @@ impl Clone for Reverse { /// /// ## Derivable /// -/// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a -/// [lexicographic](https://en.wikipedia.org/wiki/Lexicographic_order) ordering based on the top-to-bottom declaration order of the struct's members. -/// When `derive`d on enums, variants are ordered by their top-to-bottom discriminant order. -/// This means variants at the top are less than variants at the bottom. -/// Here's an example: +/// This trait can be used with `#[derive]`. +/// +/// When `derive`d on structs, it will produce a +/// [lexicographic](https://en.wikipedia.org/wiki/Lexicographic_order) ordering +/// based on the top-to-bottom declaration order of the struct's members. +/// +/// When `derive`d on enums, variants are ordered by their discriminants. +/// By default, the discriminant is smallest for variants at the top, and +/// largest for variants at the bottom. Here's an example: /// /// ``` -/// #[derive(PartialEq, PartialOrd)] -/// enum Size { -/// Small, -/// Large, +/// #[derive(PartialEq, Eq, PartialOrd, Ord)] +/// enum E { +/// Top, +/// Bottom, +/// } +/// +/// assert!(E::Top < E::Bottom); +/// ``` +/// +/// However, manually setting the discriminants can override this default +/// behavior: +/// +/// ``` +/// #[derive(PartialEq, Eq, PartialOrd, Ord)] +/// enum E { +/// Top = 2, +/// Bottom = 1, /// } /// -/// assert!(Size::Small < Size::Large); +/// assert!(E::Bottom < E::Top); /// ``` /// /// ## Lexicographical comparison @@ -852,7 +880,7 @@ impl PartialOrd for Ordering { } } -/// Trait for values that can be compared for a sort-order. +/// Trait for types that form a [partial order](https://en.wikipedia.org/wiki/Partial_order). /// /// The `lt`, `le`, `gt`, and `ge` methods of this trait can be called using /// the `<`, `<=`, `>`, and `>=` operators, respectively. @@ -895,9 +923,38 @@ impl PartialOrd for Ordering { /// /// ## Derivable /// -/// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a -/// lexicographic ordering based on the top-to-bottom declaration order of the struct's members. -/// When `derive`d on enums, variants are ordered by their top-to-bottom discriminant order. +/// This trait can be used with `#[derive]`. +/// +/// When `derive`d on structs, it will produce a +/// [lexicographic](https://en.wikipedia.org/wiki/Lexicographic_order) ordering +/// based on the top-to-bottom declaration order of the struct's members. +/// +/// When `derive`d on enums, variants are ordered by their discriminants. +/// By default, the discriminant is smallest for variants at the top, and +/// largest for variants at the bottom. Here's an example: +/// +/// ``` +/// #[derive(PartialEq, PartialOrd)] +/// enum E { +/// Top, +/// Bottom, +/// } +/// +/// assert!(E::Top < E::Bottom); +/// ``` +/// +/// However, manually setting the discriminants can override this default +/// behavior: +/// +/// ``` +/// #[derive(PartialEq, PartialOrd)] +/// enum E { +/// Top = 2, +/// Bottom = 1, +/// } +/// +/// assert!(E::Bottom < E::Top); +/// ``` /// /// ## How can I implement `PartialOrd`? /// @@ -970,8 +1027,8 @@ impl PartialOrd for Ordering { /// # Examples /// /// ``` -/// let x : u32 = 0; -/// let y : u32 = 1; +/// let x: u32 = 0; +/// let y: u32 = 1; /// /// assert_eq!(x < y, true); /// assert_eq!(x.lt(&y), true); @@ -985,9 +1042,20 @@ impl PartialOrd for Ordering { #[doc(alias = "<")] #[doc(alias = "<=")] #[doc(alias = ">=")] -#[rustc_on_unimplemented( - message = "can't compare `{Self}` with `{Rhs}`", - label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`" +#[cfg_attr( + bootstrap, + rustc_on_unimplemented( + message = "can't compare `{Self}` with `{Rhs}`", + label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`", + ) +)] +#[cfg_attr( + not(bootstrap), + rustc_on_unimplemented( + message = "can't compare `{Self}` with `{Rhs}`", + label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`", + append_const_msg, + ) )] #[rustc_diagnostic_item = "PartialOrd"] pub trait PartialOrd: PartialEq { diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 1c2e673d60..0ceedf9363 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -91,7 +91,7 @@ pub use num::FloatToInt; /// ```rust /// use std::convert::identity; /// -/// let iter = vec![Some(1), None, Some(3)].into_iter(); +/// let iter = [Some(1), None, Some(3)].into_iter(); /// let filtered = iter.filter_map(identity).collect::>(); /// assert_eq!(vec![1, 3], filtered); /// ``` @@ -426,8 +426,6 @@ pub trait TryInto: Sized { /// `TryFrom` can be implemented as follows: /// /// ``` -/// use std::convert::TryFrom; -/// /// struct GreaterThanZero(i32); /// /// impl TryFrom for GreaterThanZero { @@ -448,8 +446,6 @@ pub trait TryInto: Sized { /// As described, [`i32`] implements `TryFrom<`[`i64`]`>`: /// /// ``` -/// use std::convert::TryFrom; -/// /// let big_number = 1_000_000_000_000i64; /// // Silently truncates `big_number`, requires detecting /// // and handling the truncation after the fact. @@ -485,9 +481,10 @@ pub trait TryFrom: Sized { // As lifts over & #[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for &T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const AsRef for &T where - T: AsRef, + T: ~const AsRef, { fn as_ref(&self) -> &U { >::as_ref(*self) @@ -496,9 +493,10 @@ where // As lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for &mut T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const AsRef for &mut T where - T: AsRef, + T: ~const AsRef, { fn as_ref(&self) -> &U { >::as_ref(*self) @@ -515,9 +513,10 @@ where // AsMut lifts over &mut #[stable(feature = "rust1", since = "1.0.0")] -impl AsMut for &mut T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const AsMut for &mut T where - T: AsMut, + T: ~const AsMut, { fn as_mut(&mut self) -> &mut U { (*self).as_mut() @@ -539,6 +538,10 @@ impl const Into for T where U: ~const From, { + /// Calls `U::from(self)`. + /// + /// That is, this conversion is whatever the implementation of + /// [From]<T> for U chooses to do. fn into(self) -> U { U::from(self) } @@ -548,6 +551,7 @@ where #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] impl const From for T { + /// Returns the argument unchanged. fn from(t: T) -> T { t } @@ -571,9 +575,10 @@ impl const From for T { // TryFrom implies TryInto #[stable(feature = "try_from", since = "1.34.0")] -impl TryInto for T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const TryInto for T where - U: TryFrom, + U: ~const TryFrom, { type Error = U::Error; @@ -585,9 +590,10 @@ where // Infallible conversions are semantically equivalent to fallible conversions // with an uninhabited error type. #[stable(feature = "try_from", since = "1.34.0")] -impl TryFrom for T +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const TryFrom for T where - U: Into, + U: ~const Into, { type Error = Infallible; diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 8a2a64f8dc..e1ea988130 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -308,9 +308,21 @@ static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { loop {} }; +macro_rules! arg_new { + ($f: ident, $t: ident) => { + #[doc(hidden)] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + #[inline] + pub fn $f<'b, T: $t>(x: &'b T) -> ArgumentV1<'_> { + Self::new(x, $t::fmt) + } + }; +} + impl<'a> ArgumentV1<'a> { #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + #[inline] pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { // SAFETY: `mem::transmute(x)` is safe because // 1. `&'b T` keeps the lifetime it originated with `'b` @@ -323,6 +335,16 @@ impl<'a> ArgumentV1<'a> { unsafe { ArgumentV1 { formatter: mem::transmute(f), value: mem::transmute(x) } } } + arg_new!(new_display, Display); + arg_new!(new_debug, Debug); + arg_new!(new_octal, Octal); + arg_new!(new_lower_hex, LowerHex); + arg_new!(new_upper_hex, UpperHex); + arg_new!(new_pointer, Pointer); + arg_new!(new_binary, Binary); + arg_new!(new_lower_exp, LowerExp); + arg_new!(new_upper_exp, UpperExp); + #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] pub fn from_usize(x: &usize) -> ArgumentV1<'_> { diff --git a/library/core/src/future/into_future.rs b/library/core/src/future/into_future.rs index cac1866188..0912f8675f 100644 --- a/library/core/src/future/into_future.rs +++ b/library/core/src/future/into_future.rs @@ -13,7 +13,7 @@ pub trait IntoFuture { /// Creates a future from a value. #[unstable(feature = "into_future", issue = "67644")] - #[cfg_attr(not(bootstrap), lang = "into_future")] + #[lang = "into_future"] fn into_future(self) -> Self::Future; } diff --git a/library/core/src/future/join.rs b/library/core/src/future/join.rs index a6ffbe07d9..fa4eb0d2f3 100644 --- a/library/core/src/future/join.rs +++ b/library/core/src/future/join.rs @@ -9,7 +9,7 @@ use crate::task::{Context, Poll}; /// Polls multiple futures simultaneously, returning a tuple /// of all results once complete. /// -/// While `join!(a, b)` is similar to `(a.await, b.await)`, +/// While `join!(a, b).await` is similar to `(a.await, b.await)`, /// `join!` polls both futures concurrently and is therefore more efficient. /// /// # Examples diff --git a/library/core/src/future/pending.rs b/library/core/src/future/pending.rs index 560dd25ecf..2877e66eca 100644 --- a/library/core/src/future/pending.rs +++ b/library/core/src/future/pending.rs @@ -12,7 +12,7 @@ use crate::task::{Context, Poll}; #[stable(feature = "future_readiness_fns", since = "1.48.0")] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Pending { - _data: marker::PhantomData, + _data: marker::PhantomData T>, } /// Creates a future which never resolves, representing a computation that never @@ -43,9 +43,6 @@ impl Future for Pending { } } -#[stable(feature = "future_readiness_fns", since = "1.48.0")] -impl Unpin for Pending {} - #[stable(feature = "future_readiness_fns", since = "1.48.0")] impl Debug for Pending { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 3ff84cc967..53de8b42c0 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -602,7 +602,7 @@ pub trait BuildHasher { /// [`HashSet`]: ../../std/collections/struct.HashSet.html /// [zero-sized]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts #[stable(since = "1.7.0", feature = "build_hasher")] -pub struct BuildHasherDefault(marker::PhantomData); +pub struct BuildHasherDefault(marker::PhantomData H>); #[stable(since = "1.9.0", feature = "core_impl_debug")] impl fmt::Debug for BuildHasherDefault { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 4ecc3b0c7f..b5228397f0 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -961,7 +961,7 @@ extern "rust-intrinsic" { /// Below are common applications of `transmute` which can be replaced with safer /// constructs. /// - /// Turning raw bytes(`&[u8]`) to `u32`, `f64`, etc.: + /// Turning raw bytes (`&[u8]`) into `u32`, `f64`, etc.: /// /// ``` /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; @@ -1893,7 +1893,7 @@ extern "rust-intrinsic" { pub fn nontemporal_store(ptr: *mut T, val: T); /// See documentation of `<*const T>::offset_from` for details. - #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")] pub fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; /// See documentation of `<*const T>::guaranteed_eq` for details. @@ -1914,10 +1914,31 @@ extern "rust-intrinsic" { #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] pub fn ptr_guaranteed_ne(ptr: *const T, other: *const T) -> bool; - /// Allocate at compile time. Should not be called at runtime. + /// Allocates a block of memory at compile time. + /// At runtime, just returns a null pointer. + /// + /// # Safety + /// + /// - The `align` argument must be a power of two. + /// - At compile time, a compile error occurs if this constraint is violated. + /// - At runtime, it is not checked. #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub fn const_allocate(size: usize, align: usize) -> *mut u8; + /// Deallocates a memory which allocated by `intrinsics::const_allocate` at compile time. + /// At runtime, does nothing. + /// + /// # Safety + /// + /// - The `align` argument must be a power of two. + /// - At compile time, a compile error occurs if this constraint is violated. + /// - At runtime, it is not checked. + /// - If the `ptr` is created in an another const, this intrinsic doesn't deallocate it. + /// - If the `ptr` is pointing to a local variable, this intrinsic doesn't deallocate it. + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + #[cfg(not(bootstrap))] + pub fn const_deallocate(ptr: *mut u8, size: usize, align: usize); + /// Determines whether the raw bytes of the two values are equal. /// /// This is particularly handy for arrays, since it allows things like just diff --git a/library/core/src/iter/adapters/map.rs b/library/core/src/iter/adapters/map.rs index 449650a22f..b2ed82508d 100644 --- a/library/core/src/iter/adapters/map.rs +++ b/library/core/src/iter/adapters/map.rs @@ -19,7 +19,7 @@ use crate::ops::Try; /// you can also [`map`] backwards: /// /// ```rust -/// let v: Vec = vec![1, 2, 3].into_iter().map(|x| x + 1).rev().collect(); +/// let v: Vec = [1, 2, 3].into_iter().map(|x| x + 1).rev().collect(); /// /// assert_eq!(v, [4, 3, 2]); /// ``` @@ -32,7 +32,7 @@ use crate::ops::Try; /// ```rust /// let mut c = 0; /// -/// for pair in vec!['a', 'b', 'c'].into_iter() +/// for pair in ['a', 'b', 'c'].into_iter() /// .map(|letter| { c += 1; (letter, c) }) { /// println!("{:?}", pair); /// } @@ -49,7 +49,7 @@ use crate::ops::Try; /// ```rust /// let mut c = 0; /// -/// for pair in vec!['a', 'b', 'c'].into_iter() +/// for pair in ['a', 'b', 'c'].into_iter() /// .map(|letter| { c += 1; (letter, c) }) /// .rev() { /// println!("{:?}", pair); diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index b1b917775c..2ae92e89d6 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -1,5 +1,5 @@ use crate::iter::{InPlaceIterable, Iterator}; -use crate::ops::{ControlFlow, Try}; +use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, NeverShortCircuit, Residual, Try}; mod chain; mod cloned; @@ -24,6 +24,7 @@ mod take; mod take_while; mod zip; +#[stable(feature = "rust1", since = "1.0.0")] pub use self::{ chain::Chain, cycle::Cycle, enumerate::Enumerate, filter::Filter, filter_map::FilterMap, flatten::FlatMap, fuse::Fuse, inspect::Inspect, map::Map, peekable::Peekable, rev::Rev, @@ -127,41 +128,45 @@ pub unsafe trait SourceIter { } /// An iterator adapter that produces output as long as the underlying -/// iterator produces `Result::Ok` values. +/// iterator produces values where `Try::branch` says to `ControlFlow::Continue`. /// -/// If an error is encountered, the iterator stops and the error is -/// stored. -pub(crate) struct ResultShunt<'a, I, E> { +/// If a `ControlFlow::Break` is encountered, the iterator stops and the +/// residual is stored. +pub(crate) struct GenericShunt<'a, I, R> { iter: I, - error: &'a mut Result<(), E>, + residual: &'a mut Option, } -/// Process the given iterator as if it yielded a `T` instead of a -/// `Result`. Any errors will stop the inner iterator and -/// the overall result will be an error. -pub(crate) fn process_results(iter: I, mut f: F) -> Result +/// Process the given iterator as if it yielded a the item's `Try::Output` +/// type instead. Any `Try::Residual`s encountered will stop the inner iterator +/// and be propagated back to the overall result. +pub(crate) fn try_process(iter: I, mut f: F) -> ChangeOutputType where - I: Iterator>, - for<'a> F: FnMut(ResultShunt<'a, I, E>) -> U, + I: Iterator>, + for<'a> F: FnMut(GenericShunt<'a, I, R>) -> U, + R: Residual, { - let mut error = Ok(()); - let shunt = ResultShunt { iter, error: &mut error }; + let mut residual = None; + let shunt = GenericShunt { iter, residual: &mut residual }; let value = f(shunt); - error.map(|()| value) + match residual { + Some(r) => FromResidual::from_residual(r), + None => Try::from_output(value), + } } -impl Iterator for ResultShunt<'_, I, E> +impl Iterator for GenericShunt<'_, I, R> where - I: Iterator>, + I: Iterator>, { - type Item = T; + type Item = ::Output; fn next(&mut self) -> Option { - self.find(|_| true) + self.try_for_each(ControlFlow::Break).break_value() } fn size_hint(&self) -> (usize, Option) { - if self.error.is_err() { + if self.residual.is_some() { (0, Some(0)) } else { let (_, upper) = self.iter.size_hint(); @@ -169,17 +174,16 @@ where } } - fn try_fold(&mut self, init: B, mut f: F) -> R + fn try_fold(&mut self, init: B, mut f: F) -> T where - F: FnMut(B, Self::Item) -> R, - R: Try, + F: FnMut(B, Self::Item) -> T, + T: Try, { - let error = &mut *self.error; self.iter - .try_fold(init, |acc, x| match x { - Ok(x) => ControlFlow::from_try(f(acc, x)), - Err(e) => { - *error = Err(e); + .try_fold(init, |acc, x| match Try::branch(x) { + ControlFlow::Continue(x) => ControlFlow::from_try(f(acc, x)), + ControlFlow::Break(r) => { + *self.residual = Some(r); ControlFlow::Break(try { acc }) } }) @@ -191,17 +195,12 @@ where Self: Sized, F: FnMut(B, Self::Item) -> B, { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - move |acc, x| Ok(f(acc, x)) - } - - self.try_fold(init, ok(fold)).unwrap() + self.try_fold(init, NeverShortCircuit::wrap_mut_2(fold)).0 } } #[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl SourceIter for ResultShunt<'_, I, E> +unsafe impl SourceIter for GenericShunt<'_, I, R> where I: SourceIter, { @@ -214,11 +213,11 @@ where } } -// SAFETY: ResultShunt::next calls I::find, which has to advance `iter` in order to -// return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's guaranteed that -// at least one item will be moved out from the underlying source. +// SAFETY: GenericShunt::next calls `I::try_for_each`, which has to advance `iter` +// in order to return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's +// guaranteed that at least one item will be moved out from the underlying source. #[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl InPlaceIterable for ResultShunt<'_, I, E> where - I: Iterator> + InPlaceIterable +unsafe impl InPlaceIterable for GenericShunt<'_, I, R> where + I: Iterator> + InPlaceIterable { } diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index da459ed7c6..65f56f64db 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -417,7 +417,7 @@ pub use self::adapters::{ #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] pub use self::adapters::{Intersperse, IntersperseWith}; -pub(crate) use self::adapters::process_results; +pub(crate) use self::adapters::try_process; mod adapters; mod range; diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs index de0663141e..37b6f2e256 100644 --- a/library/core/src/iter/sources.rs +++ b/library/core/src/iter/sources.rs @@ -6,6 +6,7 @@ mod repeat; mod repeat_with; mod successors; +#[stable(feature = "rust1", since = "1.0.0")] pub use self::repeat::{repeat, Repeat}; #[stable(feature = "iter_empty", since = "1.2.0")] diff --git a/library/core/src/iter/sources/empty.rs b/library/core/src/iter/sources/empty.rs index 7abe01d17c..98734c527f 100644 --- a/library/core/src/iter/sources/empty.rs +++ b/library/core/src/iter/sources/empty.rs @@ -22,17 +22,17 @@ pub const fn empty() -> Empty { Empty(marker::PhantomData) } +// Newtype for use in `PhantomData` to avoid +// > error: const-stable function cannot use `#[feature(const_fn_fn_ptr_basics)]` +// in `const fn empty()` above. +struct FnReturning(fn() -> T); + /// An iterator that yields nothing. /// /// This `struct` is created by the [`empty()`] function. See its documentation for more. #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "iter_empty", since = "1.2.0")] -pub struct Empty(marker::PhantomData); - -#[stable(feature = "iter_empty_send_sync", since = "1.42.0")] -unsafe impl Send for Empty {} -#[stable(feature = "iter_empty_send_sync", since = "1.42.0")] -unsafe impl Sync for Empty {} +pub struct Empty(marker::PhantomData>); #[stable(feature = "core_impl_debug", since = "1.9.0")] impl fmt::Debug for Empty { diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index c2e837df5f..84d83ee396 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -167,7 +167,7 @@ where where I: Iterator>, { - iter::process_results(iter, |i| i.sum()) + iter::try_process(iter, |i| i.sum()) } } @@ -183,7 +183,7 @@ where where I: Iterator>, { - iter::process_results(iter, |i| i.product()) + iter::try_process(iter, |i| i.product()) } } @@ -210,7 +210,7 @@ where where I: Iterator>, { - iter.map(|x| x.ok_or(())).sum::>().ok() + iter::try_process(iter, |i| i.sum()) } } @@ -226,6 +226,6 @@ where where I: Iterator>, { - iter.map(|x| x.ok_or(())).product::>().ok() + iter::try_process(iter, |i| i.product()) } } diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 56fad602cf..637d7bc448 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -15,8 +15,6 @@ /// Basic usage: /// /// ``` -/// use std::iter::FromIterator; -/// /// let five_fives = std::iter::repeat(5).take(5); /// /// let v = Vec::from_iter(five_fives); @@ -37,8 +35,6 @@ /// Implementing `FromIterator` for your type: /// /// ``` -/// use std::iter::FromIterator; -/// /// // A sample collection, that's just a wrapper over Vec /// #[derive(Debug)] /// struct MyCollection(Vec); @@ -85,6 +81,32 @@ /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( + on( + _Self = "[{A}]", + message = "a value of type `{Self}` cannot be built since `{Self}` has no definite size", + label = "try explicitly collecting into a `Vec<{A}>`", + ), + on( + all( + A = "{integer}", + any( + _Self = "[i8]", + _Self = "[i16]", + _Self = "[i32]", + _Self = "[i64]", + _Self = "[i128]", + _Self = "[isize]", + _Self = "[u8]", + _Self = "[u16]", + _Self = "[u32]", + _Self = "[u64]", + _Self = "[u128]", + _Self = "[usize]" + ) + ), + message = "a value of type `{Self}` cannot be built since `{Self}` has no definite size", + label = "try explicitly collecting into a `Vec<{A}>`", + ), message = "a value of type `{Self}` cannot be built from an iterator \ over elements of type `{A}`", label = "value of type `{Self}` cannot be built from `std::iter::Iterator`" @@ -102,8 +124,6 @@ pub trait FromIterator: Sized { /// Basic usage: /// /// ``` - /// use std::iter::FromIterator; - /// /// let five_fives = std::iter::repeat(5).take(5); /// /// let v = Vec::from_iter(five_fives); @@ -130,7 +150,7 @@ pub trait FromIterator: Sized { /// Basic usage: /// /// ``` -/// let v = vec![1, 2, 3]; +/// let v = [1, 2, 3]; /// let mut iter = v.into_iter(); /// /// assert_eq!(Some(1), iter.next()); @@ -221,7 +241,7 @@ pub trait IntoIterator { /// Basic usage: /// /// ``` - /// let v = vec![1, 2, 3]; + /// let v = [1, 2, 3]; /// let mut iter = v.into_iter(); /// /// assert_eq!(Some(1), iter.next()); diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 9a9a844f41..5a361edecd 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -1,6 +1,7 @@ use crate::cmp::{self, Ordering}; use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try}; +use super::super::try_process; use super::super::TrustedRandomAccessNoCoerce; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; @@ -515,8 +516,44 @@ pub trait Iterator { /// assert_eq!((2, 'o'), zipper[2]); /// ``` /// + /// If both iterators have roughly equivalent syntax, it may be more readable to use [`zip`]: + /// + /// ``` + /// use std::iter::zip; + /// + /// let a = [1, 2, 3]; + /// let b = [2, 3, 4]; + /// + /// let mut zipped = zip( + /// a.into_iter().map(|x| x * 2).skip(1), + /// b.into_iter().map(|x| x * 2).skip(1), + /// ); + /// + /// assert_eq!(zipped.next(), Some((4, 6))); + /// assert_eq!(zipped.next(), Some((6, 8))); + /// assert_eq!(zipped.next(), None); + /// ``` + /// + /// compared to: + /// + /// ``` + /// # let a = [1, 2, 3]; + /// # let b = [2, 3, 4]; + /// # + /// let mut zipped = a + /// .into_iter() + /// .map(|x| x * 2) + /// .skip(1) + /// .zip(b.into_iter().map(|x| x * 2).skip(1)); + /// # + /// # assert_eq!(zipped.next(), Some((4, 6))); + /// # assert_eq!(zipped.next(), Some((6, 8))); + /// # assert_eq!(zipped.next(), None); + /// ``` + /// /// [`enumerate`]: Iterator::enumerate /// [`next`]: Iterator::next + /// [`zip`]: crate::iter::zip #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn zip(self, other: U) -> Zip @@ -590,7 +627,7 @@ pub trait Iterator { /// #[derive(PartialEq, Debug)] /// struct NotClone(usize); /// - /// let v = vec![NotClone(0), NotClone(1), NotClone(2)]; + /// let v = [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`. @@ -1155,8 +1192,6 @@ pub trait Iterator { /// Stopping after an initial [`None`]: /// /// ``` - /// use std::convert::TryFrom; - /// /// let a = [0, 1, 2, -3, 4, 5, -6]; /// /// let iter = a.iter().map_while(|x| u32::try_from(*x).ok()); @@ -1172,8 +1207,6 @@ pub trait Iterator { /// removed: /// /// ``` - /// use std::convert::TryFrom; - /// /// let a = [1, 2, -3, 4]; /// let mut iter = a.iter(); /// @@ -1274,7 +1307,7 @@ pub trait Iterator { /// `take` will limit itself to the size of the underlying iterator: /// /// ``` - /// let v = vec![1, 2]; + /// let v = [1, 2]; /// let mut iter = v.into_iter().take(5); /// assert_eq!(iter.next(), Some(1)); /// assert_eq!(iter.next(), Some(2)); @@ -1608,7 +1641,7 @@ pub trait Iterator { /// Basic usage: /// /// ``` - /// let mut words = vec!["hello", "world", "of", "Rust"].into_iter(); + /// let mut words = ["hello", "world", "of", "Rust"].into_iter(); /// /// // Take the first two words. /// let hello_world: Vec<_> = words.by_ref().take(2).collect(); @@ -1745,6 +1778,87 @@ pub trait Iterator { FromIterator::from_iter(self) } + /// Fallibly transforms an iterator into a collection, short circuiting if + /// a failure is encountered. + /// + /// `try_collect()` is a variation of [`collect()`][`collect`] that allows fallible + /// conversions during collection. Its main use case is simplifying conversions from + /// iterators yielding [`Option`][`Option`] into `Option>`, or similarly for other [`Try`] + /// types (e.g. [`Result`]). + /// + /// Importantly, `try_collect()` doesn't require that the outer [`Try`] type also implements [`FromIterator`]; + /// only the inner type produced on `Try::Output` must implement it. Concretely, + /// this means that collecting into `ControlFlow<_, Vec>` is valid because `Vec` implements + /// [`FromIterator`], even though [`ControlFlow`] doesn't. + /// + /// Also, if a failure is encountered during `try_collect()`, the iterator is still valid and + /// may continue to be used, in which case it will continue iterating starting after the element that + /// triggered the failure. See the last example below for an example of how this works. + /// + /// # Examples + /// Successfully collecting an iterator of `Option` into `Option>`: + /// ``` + /// #![feature(iterator_try_collect)] + /// + /// let u = vec![Some(1), Some(2), Some(3)]; + /// let v = u.into_iter().try_collect::>(); + /// assert_eq!(v, Some(vec![1, 2, 3])); + /// ``` + /// + /// Failing to collect in the same way: + /// ``` + /// #![feature(iterator_try_collect)] + /// + /// let u = vec![Some(1), Some(2), None, Some(3)]; + /// let v = u.into_iter().try_collect::>(); + /// assert_eq!(v, None); + /// ``` + /// + /// A similar example, but with `Result`: + /// ``` + /// #![feature(iterator_try_collect)] + /// + /// let u: Vec> = vec![Ok(1), Ok(2), Ok(3)]; + /// let v = u.into_iter().try_collect::>(); + /// assert_eq!(v, Ok(vec![1, 2, 3])); + /// + /// let u = vec![Ok(1), Ok(2), Err(()), Ok(3)]; + /// let v = u.into_iter().try_collect::>(); + /// assert_eq!(v, Err(())); + /// ``` + /// + /// Finally, even [`ControlFlow`] works, despite the fact that it + /// doesn't implement [`FromIterator`]. Note also that the iterator can + /// continue to be used, even if a failure is encountered: + /// + /// ``` + /// #![feature(iterator_try_collect)] + /// + /// use core::ops::ControlFlow::{Break, Continue}; + /// + /// let u = [Continue(1), Continue(2), Break(3), Continue(4), Continue(5)]; + /// let mut it = u.into_iter(); + /// + /// let v = it.try_collect::>(); + /// assert_eq!(v, Break(3)); + /// + /// let v = it.try_collect::>(); + /// assert_eq!(v, Continue(vec![4, 5])); + /// ``` + /// + /// [`collect`]: Iterator::collect + #[inline] + #[unstable(feature = "iterator_try_collect", issue = "94047")] + fn try_collect(&mut self) -> ChangeOutputType + where + Self: Sized, + ::Item: Try, + <::Item as Try>::Residual: Residual, + B: FromIterator<::Output>, + { + try_process(self, |i| i.collect()) + } + /// Consumes an iterator, creating two collections from it. /// /// The predicate passed to `partition()` can return `true`, or `false`. @@ -2704,7 +2818,7 @@ pub trait Iterator { /// incomparable. You can work around this by using [`Iterator::reduce`]: /// ``` /// assert_eq!( - /// vec![2.4, f32::NAN, 1.3] + /// [2.4, f32::NAN, 1.3] /// .into_iter() /// .reduce(f32::max) /// .unwrap(), @@ -2742,7 +2856,7 @@ pub trait Iterator { /// incomparable. You can work around this by using [`Iterator::reduce`]: /// ``` /// assert_eq!( - /// vec![2.4, f32::NAN, 1.3] + /// [2.4, f32::NAN, 1.3] /// .into_iter() /// .reduce(f32::min) /// .unwrap(), diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs index ffd745a46b..ed0fb634db 100644 --- a/library/core/src/iter/traits/mod.rs +++ b/library/core/src/iter/traits/mod.rs @@ -5,15 +5,17 @@ mod exact_size; mod iterator; mod marker; -pub use self::accum::{Product, Sum}; -pub use self::collect::{Extend, FromIterator, IntoIterator}; -pub use self::double_ended::DoubleEndedIterator; -pub use self::exact_size::ExactSizeIterator; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::iterator::Iterator; +pub use self::{ + accum::{Product, Sum}, + collect::{Extend, FromIterator, IntoIterator}, + double_ended::DoubleEndedIterator, + exact_size::ExactSizeIterator, + iterator::Iterator, + marker::{FusedIterator, TrustedLen}, +}; + #[unstable(issue = "none", feature = "inplace_iteration")] pub use self::marker::InPlaceIterable; #[unstable(feature = "trusted_step", issue = "85731")] pub use self::marker::TrustedStep; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::marker::{FusedIterator, TrustedLen}; diff --git a/library/core/src/lazy.rs b/library/core/src/lazy.rs index 788f0cce01..88826782a3 100644 --- a/library/core/src/lazy.rs +++ b/library/core/src/lazy.rs @@ -75,6 +75,7 @@ impl Eq for OnceCell {} #[unstable(feature = "once_cell", issue = "74465")] impl const From for OnceCell { + /// Creates a new `OnceCell` which already contains the given `value`. fn from(value: T) -> Self { OnceCell { inner: UnsafeCell::new(Some(value)) } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index d61800dcbb..aa1ad9362a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -60,32 +60,29 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] -#![cfg_attr( - not(bootstrap), - doc(cfg_hide( - not(test), - any(not(feature = "miri-test-libstd"), test, doctest), - no_fp_fmt_parse, - target_pointer_width = "16", - target_pointer_width = "32", - target_pointer_width = "64", - target_has_atomic = "8", - target_has_atomic = "16", - target_has_atomic = "32", - target_has_atomic = "64", - target_has_atomic = "ptr", - target_has_atomic_equal_alignment = "8", - target_has_atomic_equal_alignment = "16", - target_has_atomic_equal_alignment = "32", - target_has_atomic_equal_alignment = "64", - target_has_atomic_equal_alignment = "ptr", - target_has_atomic_load_store = "8", - target_has_atomic_load_store = "16", - target_has_atomic_load_store = "32", - target_has_atomic_load_store = "64", - target_has_atomic_load_store = "ptr", - )) -)] +#![doc(cfg_hide( + not(test), + any(not(feature = "miri-test-libstd"), test, doctest), + no_fp_fmt_parse, + target_pointer_width = "16", + target_pointer_width = "32", + target_pointer_width = "64", + target_has_atomic = "8", + target_has_atomic = "16", + target_has_atomic = "32", + target_has_atomic = "64", + target_has_atomic = "ptr", + target_has_atomic_equal_alignment = "8", + target_has_atomic_equal_alignment = "16", + target_has_atomic_equal_alignment = "32", + target_has_atomic_equal_alignment = "64", + target_has_atomic_equal_alignment = "ptr", + target_has_atomic_load_store = "8", + target_has_atomic_load_store = "16", + target_has_atomic_load_store = "32", + target_has_atomic_load_store = "64", + target_has_atomic_load_store = "ptr", +))] #![no_core] // // Lints: @@ -159,6 +156,7 @@ #![feature(associated_type_bounds)] #![feature(auto_traits)] #![feature(cfg_target_has_atomic)] +#![cfg_attr(not(bootstrap), feature(cfg_target_has_atomic_equal_alignment))] #![feature(const_fn_floating_point_arithmetic)] #![feature(const_fn_fn_ptr_basics)] #![feature(const_fn_trait_bound)] @@ -180,7 +178,6 @@ #![feature(intrinsics)] #![feature(lang_items)] #![feature(link_llvm_intrinsics)] -#![feature(llvm_asm)] #![feature(min_specialization)] #![feature(mixed_integer_ops)] #![feature(must_not_suspend)] @@ -308,6 +305,8 @@ pub mod ops; pub mod any; pub mod array; pub mod ascii; +#[unstable(feature = "async_iterator", issue = "79024")] +pub mod async_iter; pub mod cell; pub mod char; pub mod ffi; @@ -319,8 +318,6 @@ pub mod panic; pub mod panicking; pub mod pin; 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 cd2b01e7fc..628b679236 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -31,6 +31,7 @@ macro_rules! panic { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "assert_eq_macro")] #[allow_internal_unstable(core_panic)] macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => ({ @@ -80,6 +81,7 @@ macro_rules! assert_eq { /// ``` #[macro_export] #[stable(feature = "assert_ne", since = "1.13.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "assert_ne_macro")] #[allow_internal_unstable(core_panic)] macro_rules! assert_ne { ($left:expr, $right:expr $(,)?) => ({ @@ -236,6 +238,7 @@ macro_rules! debug_assert { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "debug_assert_eq_macro")] macro_rules! debug_assert_eq { ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_eq!($($arg)*); }) } @@ -261,6 +264,7 @@ macro_rules! debug_assert_eq { /// ``` #[macro_export] #[stable(feature = "assert_ne", since = "1.13.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "debug_assert_ne_macro")] macro_rules! debug_assert_ne { ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_ne!($($arg)*); }) } @@ -320,6 +324,7 @@ pub macro debug_assert_matches($($arg:tt)*) { /// ``` #[macro_export] #[stable(feature = "matches_macro", since = "1.42.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")] macro_rules! matches { ($expression:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => { match $expression { @@ -475,6 +480,7 @@ macro_rules! r#try { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "write_macro")] macro_rules! write { ($dst:expr, $($arg:tt)*) => ($dst.write_fmt($crate::format_args!($($arg)*))) } @@ -525,6 +531,7 @@ macro_rules! write { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "writeln_macro")] #[allow_internal_unstable(format_args_nl)] macro_rules! writeln { ($dst:expr $(,)?) => ( @@ -605,6 +612,7 @@ macro_rules! unreachable { #[cfg(bootstrap)] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "unreachable_macro")] #[allow_internal_unstable(core_panic)] macro_rules! unreachable { () => ({ @@ -691,6 +699,7 @@ macro_rules! unreachable { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "unimplemented_macro")] #[allow_internal_unstable(core_panic)] macro_rules! unimplemented { () => ($crate::panicking::panic("not implemented")); @@ -753,6 +762,7 @@ macro_rules! unimplemented { /// ``` #[macro_export] #[stable(feature = "todo_macro", since = "1.40.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "todo_macro")] #[allow_internal_unstable(core_panic)] macro_rules! todo { () => ($crate::panicking::panic("not yet implemented")); @@ -802,6 +812,7 @@ pub(crate) mod builtin { #[stable(feature = "compile_error_macro", since = "1.20.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "compile_error_macro")] macro_rules! compile_error { ($msg:expr $(,)?) => {{ /* compiler built-in */ }}; } @@ -851,6 +862,7 @@ pub(crate) mod builtin { /// assert_eq!(s, format!("hello {}", "world")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "format_args_macro")] #[allow_internal_unsafe] #[allow_internal_unstable(fmt_internals)] #[rustc_builtin_macro] @@ -860,7 +872,7 @@ pub(crate) mod builtin { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } - /// Same as `format_args`, but can be used in some const contexts. + /// Same as [`format_args`], but can be used in some const contexts. /// /// This macro is used by the panic macros for the `const_panic` feature. /// @@ -874,7 +886,7 @@ pub(crate) mod builtin { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } - /// Same as `format_args`, but adds a newline in the end. + /// Same as [`format_args`], but adds a newline in the end. #[unstable( feature = "format_args_nl", issue = "none", @@ -921,6 +933,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "env_macro")] macro_rules! env { ($name:expr $(,)?) => {{ /* compiler built-in */ }}; ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }}; @@ -946,6 +959,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "option_env_macro")] macro_rules! option_env { ($name:expr $(,)?) => {{ /* compiler built-in */ }}; } @@ -1005,7 +1019,6 @@ pub(crate) mod builtin { /// assert_eq!(s, b"ABCDEF"); /// # } /// ``` - #[cfg(not(bootstrap))] #[unstable(feature = "concat_bytes", issue = "87555")] #[rustc_builtin_macro] #[macro_export] @@ -1031,6 +1044,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "concat_macro")] macro_rules! concat { ($($e:expr),* $(,)?) => {{ /* compiler built-in */ }}; } @@ -1056,6 +1070,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "line_macro")] macro_rules! line { () => { /* compiler built-in */ @@ -1095,6 +1110,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "column_macro")] macro_rules! column { () => { /* compiler built-in */ @@ -1120,6 +1136,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "file_macro")] macro_rules! file { () => { /* compiler built-in */ @@ -1144,6 +1161,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "stringify_macro")] macro_rules! stringify { ($($t:tt)*) => { /* compiler built-in */ @@ -1185,6 +1203,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "include_str_macro")] macro_rules! include_str { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } @@ -1224,6 +1243,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "include_bytes_macro")] macro_rules! include_bytes { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } @@ -1248,6 +1268,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "module_path_macro")] macro_rules! module_path { () => { /* compiler built-in */ @@ -1281,6 +1302,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "cfg_macro")] macro_rules! cfg { ($($cfg:tt)*) => { /* compiler built-in */ @@ -1331,6 +1353,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] + #[cfg_attr(not(test), rustc_diagnostic_item = "include_macro")] macro_rules! include { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } @@ -1389,32 +1412,6 @@ pub(crate) mod builtin { ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; } - /// LLVM-style inline assembly. - /// - /// Read the [unstable book] for the usage. - /// - /// [unstable book]: ../unstable-book/library-features/llvm-asm.html - #[unstable( - feature = "llvm_asm", - issue = "70173", - reason = "prefer using the new asm! syntax instead" - )] - #[rustc_deprecated( - since = "1.56", - reason = "will be removed from the compiler, use asm! instead" - )] - #[rustc_builtin_macro] - #[macro_export] - macro_rules! llvm_asm { - ("assembly template" - : $("output"(operand),)* - : $("input"(operand),)* - : $("clobbers",)* - : $("options",)*) => { - /* compiler built-in */ - }; - } - /// Prints passed tokens into the standard output. #[unstable( feature = "log_syntax", diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3b0e4a31db..e38c0412a0 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,8 +1,9 @@ use crate::any::type_name; use crate::fmt; use crate::intrinsics; -use crate::mem::ManuallyDrop; +use crate::mem::{self, ManuallyDrop}; use crate::ptr; +use crate::slice; /// A wrapper type to construct uninitialized instances of `T`. /// @@ -330,7 +331,7 @@ impl MaybeUninit { /// # Examples /// /// ```no_run - /// #![feature(maybe_uninit_uninit_array, maybe_uninit_extra, maybe_uninit_slice)] + /// #![feature(maybe_uninit_uninit_array, maybe_uninit_slice)] /// /// use std::mem::MaybeUninit; /// @@ -662,7 +663,6 @@ impl MaybeUninit { /// Correct usage of this method: /// /// ```rust - /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::::uninit(); @@ -683,7 +683,6 @@ impl MaybeUninit { /// *Incorrect* usage of this method: /// /// ```rust,no_run - /// #![feature(maybe_uninit_extra)] /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::>>::uninit(); @@ -693,8 +692,8 @@ impl MaybeUninit { /// // We now created two copies of the same vector, leading to a double-free ⚠️ when /// // they both get dropped! /// ``` - #[unstable(feature = "maybe_uninit_extra", issue = "63567")] - #[rustc_const_unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init_read", issue = "63567")] #[inline(always)] #[track_caller] pub const unsafe fn assume_init_read(&self) -> T { @@ -728,7 +727,7 @@ impl MaybeUninit { /// /// [`assume_init`]: MaybeUninit::assume_init /// [`Vec`]: ../../std/vec/struct.Vec.html - #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and // satisfies all invariants of `T`. @@ -1040,7 +1039,7 @@ impl MaybeUninit { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice, vec_spare_capacity)] + /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut vec = Vec::with_capacity(32); @@ -1100,7 +1099,7 @@ impl MaybeUninit { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice, vec_spare_capacity)] + /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut vec = Vec::with_capacity(32); @@ -1162,4 +1161,126 @@ impl MaybeUninit { // SAFETY: Valid elements have just been written into `this` so it is initialized unsafe { MaybeUninit::slice_assume_init_mut(this) } } + + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. + /// + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] + /// use std::mem::MaybeUninit; + /// + /// let val = 0x12345678i32; + /// let uninit = MaybeUninit::new(val); + /// let uninit_bytes = uninit.as_bytes(); + /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(uninit_bytes) }; + /// assert_eq!(bytes, val.to_ne_bytes()); + /// ``` + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub fn as_bytes(&self) -> &[MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts(self.as_ptr() as *const MaybeUninit, mem::size_of::()) + } + } + + /// Returns the contents of this `MaybeUninit` as a mutable slice of potentially uninitialized + /// bytes. + /// + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_as_bytes)] + /// use std::mem::MaybeUninit; + /// + /// let val = 0x12345678i32; + /// let mut uninit = MaybeUninit::new(val); + /// let uninit_bytes = uninit.as_bytes_mut(); + /// if cfg!(target_endian = "little") { + /// uninit_bytes[0].write(0xcd); + /// } else { + /// uninit_bytes[3].write(0xcd); + /// } + /// let val2 = unsafe { uninit.assume_init() }; + /// assert_eq!(val2, 0x123456cd); + /// ``` + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut MaybeUninit, + mem::size_of::(), + ) + } + } + + /// Returns the contents of this slice of `MaybeUninit` as a slice of potentially uninitialized + /// bytes. + /// + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// use std::mem::MaybeUninit; + /// + /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; + /// let uninit_bytes = MaybeUninit::slice_as_bytes(&uninit); + /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(&uninit_bytes) }; + /// let val1 = u16::from_ne_bytes(bytes[0..2].try_into().unwrap()); + /// let val2 = u16::from_ne_bytes(bytes[2..4].try_into().unwrap()); + /// assert_eq!(&[val1, val2], &[0x1234u16, 0x5678u16]); + /// ``` + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts( + this.as_ptr() as *const MaybeUninit, + this.len() * mem::size_of::(), + ) + } + } + + /// Returns the contents of this mutable slice of `MaybeUninit` as a mutable slice of + /// potentially uninitialized bytes. + /// + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; + /// let uninit_bytes = MaybeUninit::slice_as_bytes_mut(&mut uninit); + /// MaybeUninit::write_slice(uninit_bytes, &[0x12, 0x34, 0x56, 0x78]); + /// let vals = unsafe { MaybeUninit::slice_assume_init_ref(&uninit) }; + /// if cfg!(target_endian = "little") { + /// assert_eq!(vals, &[0x3412u16, 0x7856u16]); + /// } else { + /// assert_eq!(vals, &[0x1234u16, 0x5678u16]); + /// } + /// ``` + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts_mut( + this.as_mut_ptr() as *mut MaybeUninit, + this.len() * mem::size_of::(), + ) + } + } } diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index 8a06a09888..de85fdd6ed 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -19,18 +19,8 @@ )] #![macro_use] -use crate::intrinsics; - /// Arithmetic operations required by bignums. pub trait FullOps: Sized { - /// Returns `(carry', v')` such that `carry' * 2^W + v' = self + other + carry`, - /// where `W` is the number of bits in `Self`. - fn full_add(self, other: Self, carry: bool) -> (bool /* carry */, Self); - - /// Returns `(carry', v')` such that `carry' * 2^W + v' = self * other + carry`, - /// where `W` is the number of bits in `Self`. - fn full_mul(self, other: Self, carry: Self) -> (Self /* carry */, Self); - /// Returns `(carry', v')` such that `carry' * 2^W + v' = self * other + other2 + carry`, /// where `W` is the number of bits in `Self`. fn full_mul_add(self, other: Self, other2: Self, carry: Self) -> (Self /* carry */, Self); @@ -45,22 +35,6 @@ macro_rules! impl_full_ops { ($($ty:ty: add($addfn:path), mul/div($bigty:ident);)*) => ( $( impl FullOps for $ty { - fn full_add(self, other: $ty, carry: bool) -> (bool, $ty) { - // This cannot overflow; the output is between `0` and `2 * 2^nbits - 1`. - // FIXME: will LLVM optimize this into ADC or similar? - let (v, carry1) = intrinsics::add_with_overflow(self, other); - let (v, carry2) = intrinsics::add_with_overflow(v, if carry {1} else {0}); - (carry1 || carry2, v) - } - - fn full_mul(self, other: $ty, carry: $ty) -> ($ty, $ty) { - // This cannot overflow; - // the output is between `0` and `2^nbits * (2^nbits - 1)`. - // FIXME: will LLVM optimize this into ADC or similar? - let v = (self as $bigty) * (other as $bigty) + (carry as $bigty); - ((v >> <$ty>::BITS) as $ty, v as $ty) - } - fn full_mul_add(self, other: $ty, other2: $ty, carry: $ty) -> ($ty, $ty) { // This cannot overflow; // the output is between `0` and `2^nbits * (2^nbits - 1)`. @@ -158,36 +132,26 @@ macro_rules! define_bignum { /// Returns the number of bits necessary to represent this value. Note that zero /// is considered to need 0 bits. pub fn bit_length(&self) -> usize { - // Skip over the most significant digits which are zero. + let digitbits = <$ty>::BITS as usize; let digits = self.digits(); - let zeros = digits.iter().rev().take_while(|&&x| x == 0).count(); - let end = digits.len() - zeros; - let nonzero = &digits[..end]; - - if nonzero.is_empty() { + // Find the most significant non-zero digit. + let msd = digits.iter().rposition(|&x| x != 0); + match msd { + Some(msd) => msd * digitbits + digits[msd].log2() as usize + 1, // There are no non-zero digits, i.e., the number is zero. - return 0; + _ => 0, } - // This could be optimized with leading_zeros() and bit shifts, but that's - // probably not worth the hassle. - let digitbits = <$ty>::BITS as usize; - let mut i = nonzero.len() * digitbits - 1; - while self.get_bit(i) == 0 { - i -= 1; - } - i + 1 } /// Adds `other` to itself and returns its own mutable reference. pub fn add<'a>(&'a mut self, other: &$name) -> &'a mut $name { use crate::cmp; use crate::iter; - use crate::num::bignum::FullOps; let mut sz = cmp::max(self.size, other.size); let mut carry = false; for (a, b) in iter::zip(&mut self.base[..sz], &other.base[..sz]) { - let (c, v) = (*a).full_add(*b, carry); + let (v, c) = (*a).carrying_add(*b, carry); *a = v; carry = c; } @@ -200,13 +164,11 @@ macro_rules! define_bignum { } pub fn add_small(&mut self, other: $ty) -> &mut $name { - use crate::num::bignum::FullOps; - - let (mut carry, v) = self.base[0].full_add(other, false); + let (v, mut carry) = self.base[0].carrying_add(other, false); self.base[0] = v; let mut i = 1; while carry { - let (c, v) = self.base[i].full_add(0, carry); + let (v, c) = self.base[i].carrying_add(0, carry); self.base[i] = v; carry = c; i += 1; @@ -221,12 +183,11 @@ macro_rules! define_bignum { pub fn sub<'a>(&'a mut self, other: &$name) -> &'a mut $name { use crate::cmp; use crate::iter; - use crate::num::bignum::FullOps; let sz = cmp::max(self.size, other.size); let mut noborrow = true; for (a, b) in iter::zip(&mut self.base[..sz], &other.base[..sz]) { - let (c, v) = (*a).full_add(!*b, noborrow); + let (v, c) = (*a).carrying_add(!*b, noborrow); *a = v; noborrow = c; } @@ -238,12 +199,10 @@ macro_rules! define_bignum { /// Multiplies itself by a digit-sized `other` and returns its own /// mutable reference. pub fn mul_small(&mut self, other: $ty) -> &mut $name { - use crate::num::bignum::FullOps; - let mut sz = self.size; let mut carry = 0; for a in &mut self.base[..sz] { - let (c, v) = (*a).full_mul(other, carry); + let (v, c) = (*a).carrying_mul(other, carry); *a = v; carry = c; } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 85ceede5b9..d8dcfdafa8 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -675,7 +675,7 @@ impl f32 { /// Returns the maximum of the two numbers. /// /// Follows the IEEE-754 2008 semantics for maxNum, except for handling of signaling NaNs. - /// This matches the behavior of libm’s fmin. + /// This matches the behavior of libm’s fmax. /// /// ``` /// let x = 1.0f32; @@ -1008,29 +1008,37 @@ impl f32 { Self::from_bits(u32::from_ne_bytes(bytes)) } - /// Returns an ordering between self and other values. + /// Return the ordering between `self` and `other`. + /// /// Unlike the standard partial comparison between floating point numbers, /// this comparison always produces an ordering in accordance to - /// the totalOrder predicate as defined in IEEE 754 (2008 revision) - /// floating point standard. The values are ordered in following order: - /// - Negative quiet NaN - /// - Negative signaling NaN - /// - Negative infinity - /// - Negative numbers - /// - Negative subnormal numbers - /// - Negative zero - /// - Positive zero - /// - Positive subnormal numbers - /// - Positive numbers - /// - Positive infinity - /// - Positive signaling NaN - /// - Positive quiet NaN - /// - /// Note that this function does not always agree with the [`PartialOrd`] - /// and [`PartialEq`] implementations of `f32`. In particular, they regard - /// negative and positive zero as equal, while `total_cmp` doesn't. + /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in the following sequence: + /// + /// - negative quiet NaN + /// - negative signaling NaN + /// - negative infinity + /// - negative numbers + /// - negative subnormal numbers + /// - negative zero + /// - positive zero + /// - positive subnormal numbers + /// - positive numbers + /// - positive infinity + /// - positive signaling NaN + /// - positive quiet NaN. + /// + /// The ordering established by this function does not always agree with the + /// [`PartialOrd`] and [`PartialEq`] implementations of `f32`. For example, + /// they consider negative and positive zero equal, while `total_cmp` + /// doesn't. + /// + /// The interpretation of the signaling NaN bit follows the definition in + /// the IEEE 754 standard, which may not match the interpretation by some of + /// the older, non-conformant (e.g. MIPS) hardware implementations. /// /// # Example + /// /// ``` /// #![feature(total_cmp)] /// struct GoodBoy { diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 4049c95b13..7c2f51ff64 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -691,7 +691,7 @@ impl f64 { /// Returns the maximum of the two numbers. /// /// Follows the IEEE-754 2008 semantics for maxNum, except for handling of signaling NaNs. - /// This matches the behavior of libm’s fmin. + /// This matches the behavior of libm’s fmax. /// /// ``` /// let x = 1.0_f64; @@ -1024,29 +1024,37 @@ impl f64 { Self::from_bits(u64::from_ne_bytes(bytes)) } - /// Returns an ordering between self and other values. + /// Return the ordering between `self` and `other`. + /// /// Unlike the standard partial comparison between floating point numbers, /// this comparison always produces an ordering in accordance to - /// the totalOrder predicate as defined in IEEE 754 (2008 revision) - /// floating point standard. The values are ordered in following order: - /// - Negative quiet NaN - /// - Negative signaling NaN - /// - Negative infinity - /// - Negative numbers - /// - Negative subnormal numbers - /// - Negative zero - /// - Positive zero - /// - Positive subnormal numbers - /// - Positive numbers - /// - Positive infinity - /// - Positive signaling NaN - /// - Positive quiet NaN - /// - /// Note that this function does not always agree with the [`PartialOrd`] - /// and [`PartialEq`] implementations of `f64`. In particular, they regard - /// negative and positive zero as equal, while `total_cmp` doesn't. + /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in the following sequence: + /// + /// - negative quiet NaN + /// - negative signaling NaN + /// - negative infinity + /// - negative numbers + /// - negative subnormal numbers + /// - negative zero + /// - positive zero + /// - positive subnormal numbers + /// - positive numbers + /// - positive infinity + /// - positive signaling NaN + /// - positive quiet NaN. + /// + /// The ordering established by this function does not always agree with the + /// [`PartialOrd`] and [`PartialEq`] implementations of `f64`. For example, + /// they consider negative and positive zero equal, while `total_cmp` + /// doesn't. + /// + /// The interpretation of the signaling NaN bit follows the definition in + /// the IEEE 754 standard, which may not match the interpretation by some of + /// the older, non-conformant (e.g. MIPS) hardware implementations. /// /// # Example + /// /// ``` /// #![feature(total_cmp)] /// struct GoodBoy { diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs index a8455fb355..cc26c04a5d 100644 --- a/library/core/src/num/int_log10.rs +++ b/library/core/src/num/int_log10.rs @@ -1,141 +1,140 @@ -mod unchecked { - // 0 < val <= u8::MAX - #[inline] - pub const fn u8(val: u8) -> u32 { - let val = val as u32; - - // For better performance, avoid branches by assembling the solution - // in the bits above the low 8 bits. - - // Adding c1 to val gives 10 in the top bits for val < 10, 11 for val >= 10 - const C1: u32 = 0b11_00000000 - 10; // 758 - // Adding c2 to val gives 01 in the top bits for val < 100, 10 for val >= 100 - const C2: u32 = 0b10_00000000 - 100; // 412 - - // Value of top bits: - // +c1 +c2 1&2 - // 0..=9 10 01 00 = 0 - // 10..=99 11 01 01 = 1 - // 100..=255 11 10 10 = 2 - ((val + C1) & (val + C2)) >> 8 - } +/// These functions compute the integer logarithm of their type, assuming +/// that someone has already checked that the the value is strictly positive. + +// 0 < val <= u8::MAX +#[inline] +pub const fn u8(val: u8) -> u32 { + let val = val as u32; + + // For better performance, avoid branches by assembling the solution + // in the bits above the low 8 bits. + + // Adding c1 to val gives 10 in the top bits for val < 10, 11 for val >= 10 + const C1: u32 = 0b11_00000000 - 10; // 758 + // Adding c2 to val gives 01 in the top bits for val < 100, 10 for val >= 100 + const C2: u32 = 0b10_00000000 - 100; // 412 + + // Value of top bits: + // +c1 +c2 1&2 + // 0..=9 10 01 00 = 0 + // 10..=99 11 01 01 = 1 + // 100..=255 11 10 10 = 2 + ((val + C1) & (val + C2)) >> 8 +} - // 0 < val < 100_000 - #[inline] - const fn less_than_5(val: u32) -> u32 { - // Similar to u8, when adding one of these constants to val, - // we get two possible bit patterns above the low 17 bits, - // depending on whether val is below or above the threshold. - const C1: u32 = 0b011_00000000000000000 - 10; // 393206 - const C2: u32 = 0b100_00000000000000000 - 100; // 524188 - const C3: u32 = 0b111_00000000000000000 - 1000; // 916504 - const C4: u32 = 0b100_00000000000000000 - 10000; // 514288 - - // Value of top bits: - // +c1 +c2 1&2 +c3 +c4 3&4 ^ - // 0..=9 010 011 010 110 011 010 000 = 0 - // 10..=99 011 011 011 110 011 010 001 = 1 - // 100..=999 011 100 000 110 011 010 010 = 2 - // 1000..=9999 011 100 000 111 011 011 011 = 3 - // 10000..=99999 011 100 000 111 100 100 100 = 4 - (((val + C1) & (val + C2)) ^ ((val + C3) & (val + C4))) >> 17 - } +// 0 < val < 100_000 +#[inline] +const fn less_than_5(val: u32) -> u32 { + // Similar to u8, when adding one of these constants to val, + // we get two possible bit patterns above the low 17 bits, + // depending on whether val is below or above the threshold. + const C1: u32 = 0b011_00000000000000000 - 10; // 393206 + const C2: u32 = 0b100_00000000000000000 - 100; // 524188 + const C3: u32 = 0b111_00000000000000000 - 1000; // 916504 + const C4: u32 = 0b100_00000000000000000 - 10000; // 514288 + + // Value of top bits: + // +c1 +c2 1&2 +c3 +c4 3&4 ^ + // 0..=9 010 011 010 110 011 010 000 = 0 + // 10..=99 011 011 011 110 011 010 001 = 1 + // 100..=999 011 100 000 110 011 010 010 = 2 + // 1000..=9999 011 100 000 111 011 011 011 = 3 + // 10000..=99999 011 100 000 111 100 100 100 = 4 + (((val + C1) & (val + C2)) ^ ((val + C3) & (val + C4))) >> 17 +} - // 0 < val <= u16::MAX - #[inline] - pub const fn u16(val: u16) -> u32 { - less_than_5(val as u32) - } +// 0 < val <= u16::MAX +#[inline] +pub const fn u16(val: u16) -> u32 { + less_than_5(val as u32) +} - // 0 < val <= u32::MAX - #[inline] - pub const fn u32(mut val: u32) -> u32 { - let mut log = 0; - if val >= 100_000 { - val /= 100_000; - log += 5; - } - log + less_than_5(val) +// 0 < val <= u32::MAX +#[inline] +pub const fn u32(mut val: u32) -> u32 { + let mut log = 0; + if val >= 100_000 { + val /= 100_000; + log += 5; } + log + less_than_5(val) +} - // 0 < val <= u64::MAX - #[inline] - pub const fn u64(mut val: u64) -> u32 { - let mut log = 0; - if val >= 10_000_000_000 { - val /= 10_000_000_000; - log += 10; - } - if val >= 100_000 { - val /= 100_000; - log += 5; - } - log + less_than_5(val as u32) +// 0 < val <= u64::MAX +#[inline] +pub const fn u64(mut val: u64) -> u32 { + let mut log = 0; + if val >= 10_000_000_000 { + val /= 10_000_000_000; + log += 10; } - - // 0 < val <= u128::MAX - #[inline] - pub const fn u128(mut val: u128) -> u32 { - let mut log = 0; - if val >= 100_000_000_000_000_000_000_000_000_000_000 { - val /= 100_000_000_000_000_000_000_000_000_000_000; - log += 32; - return log + u32(val as u32); - } - if val >= 10_000_000_000_000_000 { - val /= 10_000_000_000_000_000; - log += 16; - } - log + u64(val as u64) + if val >= 100_000 { + val /= 100_000; + log += 5; } + log + less_than_5(val as u32) +} - // 0 < val <= i8::MAX - #[inline] - pub const fn i8(val: i8) -> u32 { - u8(val as u8) +// 0 < val <= u128::MAX +#[inline] +pub const fn u128(mut val: u128) -> u32 { + let mut log = 0; + if val >= 100_000_000_000_000_000_000_000_000_000_000 { + val /= 100_000_000_000_000_000_000_000_000_000_000; + log += 32; + return log + u32(val as u32); } - - // 0 < val <= i16::MAX - #[inline] - pub const fn i16(val: i16) -> u32 { - u16(val as u16) + if val >= 10_000_000_000_000_000 { + val /= 10_000_000_000_000_000; + log += 16; } + log + u64(val as u64) +} - // 0 < val <= i32::MAX - #[inline] - pub const fn i32(val: i32) -> u32 { - u32(val as u32) - } +#[cfg(target_pointer_width = "16")] +#[inline] +pub const fn usize(val: usize) -> u32 { + u16(val as _) +} - // 0 < val <= i64::MAX - #[inline] - pub const fn i64(val: i64) -> u32 { - u64(val as u64) - } +#[cfg(target_pointer_width = "32")] +#[inline] +pub const fn usize(val: usize) -> u32 { + u32(val as _) +} - // 0 < val <= i128::MAX - #[inline] - pub const fn i128(val: i128) -> u32 { - u128(val as u128) - } +#[cfg(target_pointer_width = "64")] +#[inline] +pub const fn usize(val: usize) -> u32 { + u64(val as _) +} + +// 0 < val <= i8::MAX +#[inline] +pub const fn i8(val: i8) -> u32 { + u8(val as u8) } -macro_rules! impl_checked { - ($T:ident) => { - #[inline] - pub const fn $T(val: $T) -> Option { - if val > 0 { Some(unchecked::$T(val)) } else { None } - } - }; +// 0 < val <= i16::MAX +#[inline] +pub const fn i16(val: i16) -> u32 { + u16(val as u16) } -impl_checked! { u8 } -impl_checked! { u16 } -impl_checked! { u32 } -impl_checked! { u64 } -impl_checked! { u128 } -impl_checked! { i8 } -impl_checked! { i16 } -impl_checked! { i32 } -impl_checked! { i64 } -impl_checked! { i128 } +// 0 < val <= i32::MAX +#[inline] +pub const fn i32(val: i32) -> u32 { + u32(val as u32) +} + +// 0 < val <= i64::MAX +#[inline] +pub const fn i64(val: i64) -> u32 { + u64(val as u64) +} + +// 0 < val <= i128::MAX +#[inline] +pub const fn i128(val: i128) -> u32 { + u128(val as u128) +} diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index e6ae4afd7c..199af08156 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -4,7 +4,7 @@ macro_rules! int_impl { $reversed:expr, $le_bytes:expr, $be_bytes:expr, $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { /// The smallest value that can be represented by this integer type, - #[doc = concat!("-2", $BITS_MINUS_ONE, ".")] + #[doc = concat!("−2", $BITS_MINUS_ONE, ".")] /// /// # Examples /// @@ -17,7 +17,7 @@ macro_rules! int_impl { pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self; /// The largest value that can be represented by this integer type, - #[doc = concat!("2", $BITS_MINUS_ONE, " - 1.")] + #[doc = concat!("2", $BITS_MINUS_ONE, " − 1.")] /// /// # Examples /// @@ -1064,6 +1064,7 @@ macro_rules! int_impl { /// /// ``` #[stable(feature = "saturating_div", since = "1.58.0")] + #[rustc_const_stable(feature = "saturating_div", since = "1.58.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2362,7 +2363,11 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn checked_log10(self) -> Option { - int_log10::$ActualT(self as $ActualT) + if self > 0 { + Some(int_log10::$ActualT(self as $ActualT)) + } else { + None + } } /// Computes the absolute value of `self`. @@ -2415,14 +2420,14 @@ macro_rules! int_impl { /// Basic usage: /// /// ``` - /// #![feature(int_abs_diff)] #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(80), 20", stringify!($UnsignedT), ");")] #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(110), 10", stringify!($UnsignedT), ");")] #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").abs_diff(80), 180", stringify!($UnsignedT), ");")] #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").abs_diff(-120), 20", stringify!($UnsignedT), ");")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.abs_diff(", stringify!($SelfT), "::MAX), ", stringify!($UnsignedT), "::MAX);")] /// ``` - #[unstable(feature = "int_abs_diff", issue = "89492")] + #[stable(feature = "int_abs_diff", since = "1.60.0")] + #[rustc_const_stable(feature = "int_abs_diff", since = "1.60.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2602,8 +2607,6 @@ macro_rules! int_impl { /// 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; @@ -2633,8 +2636,6 @@ macro_rules! int_impl { /// 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; @@ -2675,8 +2676,6 @@ macro_rules! int_impl { /// 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; diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index e3eab07b9d..72105888f9 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -264,7 +264,7 @@ const ASCII_CASE_MASK: u8 = 0b0010_0000; #[lang = "u8"] impl u8 { - uint_impl! { u8, u8, i8, 8, 255, 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]", + uint_impl! { u8, u8, i8, NonZeroU8, 8, 255, 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]", "[0x12]", "", "" } widening_impl! { u8, u16, 8, unsigned } @@ -791,7 +791,6 @@ impl u8 { /// # Examples /// /// ``` - /// #![feature(inherent_ascii_escape)] /// /// assert_eq!("0", b'0'.escape_ascii().to_string()); /// assert_eq!("\\t", b'\t'.escape_ascii().to_string()); @@ -804,30 +803,35 @@ impl u8 { /// ``` #[must_use = "this returns the escaped byte as an iterator, \ without modifying the original"] - #[unstable(feature = "inherent_ascii_escape", issue = "77174")] + #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] #[inline] - pub fn escape_ascii(&self) -> ascii::EscapeDefault { - ascii::escape_default(*self) + pub fn escape_ascii(self) -> ascii::EscapeDefault { + ascii::escape_default(self) + } + + pub(crate) fn is_utf8_char_boundary(self) -> bool { + // This is bit magic equivalent to: b < 128 || b >= 192 + (self as i8) >= -0x40 } } #[lang = "u16"] impl u16 { - uint_impl! { u16, u16, i16, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", + uint_impl! { u16, u16, i16, NonZeroU16, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" } widening_impl! { u16, u32, 16, unsigned } } #[lang = "u32"] impl u32 { - uint_impl! { u32, u32, i32, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678", + uint_impl! { u32, u32, i32, NonZeroU32, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } widening_impl! { u32, u64, 32, unsigned } } #[lang = "u64"] impl u64 { - uint_impl! { u64, u64, i64, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa", + uint_impl! { u64, u64, i64, NonZeroU64, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", @@ -837,7 +841,7 @@ impl u64 { #[lang = "u128"] impl u128 { - uint_impl! { u128, u128, i128, 128, 340282366920938463463374607431768211455, 16, + uint_impl! { u128, u128, i128, NonZeroU128, 128, 340282366920938463463374607431768211455, 16, "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ @@ -850,7 +854,7 @@ impl u128 { #[cfg(target_pointer_width = "16")] #[lang = "usize"] impl usize { - uint_impl! { usize, u16, isize, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", + uint_impl! { usize, u16, isize, NonZeroUsize, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } widening_impl! { usize, u32, 16, unsigned } @@ -858,7 +862,7 @@ impl usize { #[cfg(target_pointer_width = "32")] #[lang = "usize"] impl usize { - uint_impl! { usize, u32, isize, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678", + uint_impl! { usize, u32, isize, NonZeroUsize, 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!() } widening_impl! { usize, u64, 32, unsigned } @@ -867,7 +871,7 @@ impl usize { #[cfg(target_pointer_width = "64")] #[lang = "usize"] impl usize { - uint_impl! { usize, u64, isize, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa", + uint_impl! { usize, u64, isize, NonZeroUsize, 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 8f895c33a6..1ebd1c58f2 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -302,7 +302,7 @@ nonzero_integers_div! { // A bunch of methods for unsigned nonzero types only. macro_rules! nonzero_unsigned_operations { - ( $( $Ty: ident($Int: ty); )+ ) => { + ( $( $Ty: ident($Int: ident); )+ ) => { $( impl $Ty { /// Add an unsigned integer to a non-zero value. @@ -442,6 +442,56 @@ macro_rules! nonzero_unsigned_operations { None } } + + /// Returns the base 2 logarithm of the number, rounded down. + /// + /// This is the same operation as + #[doc = concat!("[`", stringify!($Int), "::log2`],")] + /// except that it has no failure cases to worry about + /// since this value can never be zero. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_log)] + #[doc = concat!("# use std::num::", stringify!($Ty), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(7).unwrap().log2(), 2);")] + #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(8).unwrap().log2(), 3);")] + #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(9).unwrap().log2(), 3);")] + /// ``` + #[unstable(feature = "int_log", issue = "70887")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn log2(self) -> u32 { + <$Int>::BITS - 1 - self.leading_zeros() + } + + /// Returns the base 10 logarithm of the number, rounded down. + /// + /// This is the same operation as + #[doc = concat!("[`", stringify!($Int), "::log10`],")] + /// except that it has no failure cases to worry about + /// since this value can never be zero. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_log)] + #[doc = concat!("# use std::num::", stringify!($Ty), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(99).unwrap().log10(), 1);")] + #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(100).unwrap().log10(), 2);")] + #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(101).unwrap().log10(), 2);")] + /// ``` + #[unstable(feature = "int_log", issue = "70887")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn log10(self) -> u32 { + super::int_log10::$Int(self.0) + } } )+ } @@ -922,6 +972,7 @@ macro_rules! nonzero_unsigned_is_power_of_two { /// ``` #[must_use] #[stable(feature = "nonzero_is_power_of_two", since = "1.59.0")] + #[rustc_const_stable(feature = "nonzero_is_power_of_two", since = "1.59.0")] #[inline] pub const fn is_power_of_two(self) -> bool { // LLVM 11 normalizes `unchecked_sub(x, 1) & x == 0` to the implementation seen here. diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs index d9b14c82e9..8982473b2d 100644 --- a/library/core/src/num/saturating.rs +++ b/library/core/src/num/saturating.rs @@ -226,6 +226,15 @@ macro_rules! saturating_impl { } forward_ref_op_assign! { impl AddAssign, add_assign for Saturating<$t>, Saturating<$t> } + #[unstable(feature = "saturating_int_assign_impl", issue = "92354")] + impl AddAssign<$t> for Saturating<$t> { + #[inline] + fn add_assign(&mut self, other: $t) { + *self = *self + Saturating(other); + } + } + forward_ref_op_assign! { impl AddAssign, add_assign for Saturating<$t>, $t } + #[unstable(feature = "saturating_int_impl", issue = "87920")] impl Sub for Saturating<$t> { type Output = Saturating<$t>; @@ -247,6 +256,15 @@ macro_rules! saturating_impl { } forward_ref_op_assign! { impl SubAssign, sub_assign for Saturating<$t>, Saturating<$t> } + #[unstable(feature = "saturating_int_assign_impl", issue = "92354")] + impl SubAssign<$t> for Saturating<$t> { + #[inline] + fn sub_assign(&mut self, other: $t) { + *self = *self - Saturating(other); + } + } + forward_ref_op_assign! { impl SubAssign, sub_assign for Saturating<$t>, $t } + #[unstable(feature = "saturating_int_impl", issue = "87920")] impl Mul for Saturating<$t> { type Output = Saturating<$t>; @@ -268,6 +286,15 @@ macro_rules! saturating_impl { } forward_ref_op_assign! { impl MulAssign, mul_assign for Saturating<$t>, Saturating<$t> } + #[unstable(feature = "saturating_int_assign_impl", issue = "92354")] + impl MulAssign<$t> for Saturating<$t> { + #[inline] + fn mul_assign(&mut self, other: $t) { + *self = *self * Saturating(other); + } + } + forward_ref_op_assign! { impl MulAssign, mul_assign for Saturating<$t>, $t } + /// # Examples /// /// Basic usage: @@ -299,6 +326,7 @@ macro_rules! saturating_impl { forward_ref_binop! { impl Div, div for Saturating<$t>, Saturating<$t>, #[unstable(feature = "saturating_int_impl", issue = "87920")] } + #[unstable(feature = "saturating_int_impl", issue = "87920")] impl DivAssign for Saturating<$t> { #[inline] @@ -308,6 +336,15 @@ macro_rules! saturating_impl { } forward_ref_op_assign! { impl DivAssign, div_assign for Saturating<$t>, Saturating<$t> } + #[unstable(feature = "saturating_int_assign_impl", issue = "92354")] + impl DivAssign<$t> for Saturating<$t> { + #[inline] + fn div_assign(&mut self, other: $t) { + *self = *self / Saturating(other); + } + } + forward_ref_op_assign! { impl DivAssign, div_assign for Saturating<$t>, $t } + #[unstable(feature = "saturating_int_impl", issue = "87920")] impl Rem for Saturating<$t> { type Output = Saturating<$t>; @@ -329,6 +366,15 @@ macro_rules! saturating_impl { } forward_ref_op_assign! { impl RemAssign, rem_assign for Saturating<$t>, Saturating<$t> } + #[unstable(feature = "saturating_int_assign_impl", issue = "92354")] + impl RemAssign<$t> for Saturating<$t> { + #[inline] + fn rem_assign(&mut self, other: $t) { + *self = *self % Saturating(other); + } + } + forward_ref_op_assign! { impl RemAssign, rem_assign for Saturating<$t>, $t } + #[unstable(feature = "saturating_int_impl", issue = "87920")] impl Not for Saturating<$t> { type Output = Saturating<$t>; @@ -362,6 +408,15 @@ macro_rules! saturating_impl { } forward_ref_op_assign! { impl BitXorAssign, bitxor_assign for Saturating<$t>, Saturating<$t> } + #[unstable(feature = "saturating_int_assign_impl", issue = "92354")] + impl BitXorAssign<$t> for Saturating<$t> { + #[inline] + fn bitxor_assign(&mut self, other: $t) { + *self = *self ^ Saturating(other); + } + } + forward_ref_op_assign! { impl BitXorAssign, bitxor_assign for Saturating<$t>, $t } + #[unstable(feature = "saturating_int_impl", issue = "87920")] impl BitOr for Saturating<$t> { type Output = Saturating<$t>; @@ -383,6 +438,15 @@ macro_rules! saturating_impl { } forward_ref_op_assign! { impl BitOrAssign, bitor_assign for Saturating<$t>, Saturating<$t> } + #[unstable(feature = "saturating_int_assign_impl", issue = "92354")] + impl BitOrAssign<$t> for Saturating<$t> { + #[inline] + fn bitor_assign(&mut self, other: $t) { + *self = *self | Saturating(other); + } + } + forward_ref_op_assign! { impl BitOrAssign, bitor_assign for Saturating<$t>, $t } + #[unstable(feature = "saturating_int_impl", issue = "87920")] impl BitAnd for Saturating<$t> { type Output = Saturating<$t>; @@ -404,6 +468,15 @@ macro_rules! saturating_impl { } forward_ref_op_assign! { impl BitAndAssign, bitand_assign for Saturating<$t>, Saturating<$t> } + #[unstable(feature = "saturating_int_assign_impl", issue = "92354")] + impl BitAndAssign<$t> for Saturating<$t> { + #[inline] + fn bitand_assign(&mut self, other: $t) { + *self = *self & Saturating(other); + } + } + forward_ref_op_assign! { impl BitAndAssign, bitand_assign for Saturating<$t>, $t } + )*) } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 3cc454baf3..feec448ebb 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1,5 +1,6 @@ macro_rules! uint_impl { - ($SelfT:ty, $ActualT:ident, $SignedT:ident, $BITS:expr, $MaxV:expr, + ($SelfT:ty, $ActualT:ident, $SignedT:ident, $NonZeroT:ident, + $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) => { @@ -16,7 +17,7 @@ macro_rules! uint_impl { pub const MIN: Self = 0; /// The largest value that can be represented by this integer type, - #[doc = concat!("2", $BITS, " - 1.")] + #[doc = concat!("2", $BITS, " − 1.")] /// /// # Examples /// @@ -839,12 +840,10 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_log2(self) -> Option { - if self <= 0 { - None + if let Some(x) = <$NonZeroT>::new(self) { + Some(x.log2()) } else { - // SAFETY: We just checked that this number is positive - let log = (Self::BITS - 1) - unsafe { intrinsics::ctlz_nonzero(self) as u32 }; - Some(log) + None } } @@ -863,7 +862,11 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_log10(self) -> Option { - int_log10::$ActualT(self as $ActualT) + if let Some(x) = <$NonZeroT>::new(self) { + Some(x.log10()) + } else { + None + } } /// Checked negation. Computes `-self`, returning `None` unless `self == @@ -1129,6 +1132,7 @@ macro_rules! uint_impl { /// /// ``` #[stable(feature = "saturating_div", since = "1.58.0")] + #[rustc_const_stable(feature = "saturating_div", since = "1.58.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1631,11 +1635,11 @@ macro_rules! uint_impl { /// Basic usage: /// /// ``` - /// #![feature(int_abs_diff)] #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(80), 20", stringify!($SelfT), ");")] #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(110), 10", stringify!($SelfT), ");")] /// ``` - #[unstable(feature = "int_abs_diff", issue = "89492")] + #[stable(feature = "int_abs_diff", since = "1.60.0")] + #[rustc_const_stable(feature = "int_abs_diff", since = "1.60.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2323,8 +2327,6 @@ macro_rules! uint_impl { /// 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; @@ -2354,8 +2356,6 @@ macro_rules! uint_impl { /// 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; @@ -2396,8 +2396,6 @@ macro_rules! uint_impl { /// 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; diff --git a/library/core/src/num/wrapping.rs b/library/core/src/num/wrapping.rs index a0e42c51e4..5353d900e7 100644 --- a/library/core/src/num/wrapping.rs +++ b/library/core/src/num/wrapping.rs @@ -239,6 +239,16 @@ macro_rules! wrapping_impl { } forward_ref_op_assign! { impl const AddAssign, add_assign for Wrapping<$t>, Wrapping<$t> } + #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] + impl const AddAssign<$t> for Wrapping<$t> { + #[inline] + fn add_assign(&mut self, other: $t) { + *self = *self + Wrapping(other); + } + } + forward_ref_op_assign! { impl const AddAssign, add_assign for Wrapping<$t>, $t } + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const Sub for Wrapping<$t> { @@ -262,6 +272,16 @@ macro_rules! wrapping_impl { } forward_ref_op_assign! { impl const SubAssign, sub_assign for Wrapping<$t>, Wrapping<$t> } + #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] + impl const SubAssign<$t> for Wrapping<$t> { + #[inline] + fn sub_assign(&mut self, other: $t) { + *self = *self - Wrapping(other); + } + } + forward_ref_op_assign! { impl const SubAssign, sub_assign for Wrapping<$t>, $t } + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const Mul for Wrapping<$t> { @@ -285,6 +305,16 @@ macro_rules! wrapping_impl { } forward_ref_op_assign! { impl const MulAssign, mul_assign for Wrapping<$t>, Wrapping<$t> } + #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] + impl const MulAssign<$t> for Wrapping<$t> { + #[inline] + fn mul_assign(&mut self, other: $t) { + *self = *self * Wrapping(other); + } + } + forward_ref_op_assign! { impl const MulAssign, mul_assign for Wrapping<$t>, $t } + #[stable(feature = "wrapping_div", since = "1.3.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const Div for Wrapping<$t> { @@ -308,6 +338,16 @@ macro_rules! wrapping_impl { } forward_ref_op_assign! { impl const DivAssign, div_assign for Wrapping<$t>, Wrapping<$t> } + #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] + impl const DivAssign<$t> for Wrapping<$t> { + #[inline] + fn div_assign(&mut self, other: $t) { + *self = *self / Wrapping(other); + } + } + forward_ref_op_assign! { impl const DivAssign, div_assign for Wrapping<$t>, $t } + #[stable(feature = "wrapping_impls", since = "1.7.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const Rem for Wrapping<$t> { @@ -331,6 +371,16 @@ macro_rules! wrapping_impl { } forward_ref_op_assign! { impl const RemAssign, rem_assign for Wrapping<$t>, Wrapping<$t> } + #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] + impl const RemAssign<$t> for Wrapping<$t> { + #[inline] + fn rem_assign(&mut self, other: $t) { + *self = *self % Wrapping(other); + } + } + forward_ref_op_assign! { impl const RemAssign, rem_assign for Wrapping<$t>, $t } + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const Not for Wrapping<$t> { @@ -367,6 +417,16 @@ macro_rules! wrapping_impl { } forward_ref_op_assign! { impl const BitXorAssign, bitxor_assign for Wrapping<$t>, Wrapping<$t> } + #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] + impl const BitXorAssign<$t> for Wrapping<$t> { + #[inline] + fn bitxor_assign(&mut self, other: $t) { + *self = *self ^ Wrapping(other); + } + } + forward_ref_op_assign! { impl const BitXorAssign, bitxor_assign for Wrapping<$t>, $t } + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const BitOr for Wrapping<$t> { @@ -390,6 +450,16 @@ macro_rules! wrapping_impl { } forward_ref_op_assign! { impl const BitOrAssign, bitor_assign for Wrapping<$t>, Wrapping<$t> } + #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] + impl const BitOrAssign<$t> for Wrapping<$t> { + #[inline] + fn bitor_assign(&mut self, other: $t) { + *self = *self | Wrapping(other); + } + } + forward_ref_op_assign! { impl const BitOrAssign, bitor_assign for Wrapping<$t>, $t } + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const BitAnd for Wrapping<$t> { @@ -413,6 +483,16 @@ macro_rules! wrapping_impl { } forward_ref_op_assign! { impl const BitAndAssign, bitand_assign for Wrapping<$t>, Wrapping<$t> } + #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")] + #[rustc_const_unstable(feature = "const_ops", issue = "90080")] + impl const BitAndAssign<$t> for Wrapping<$t> { + #[inline] + fn bitand_assign(&mut self, other: $t) { + *self = *self & Wrapping(other); + } + } + forward_ref_op_assign! { impl const BitAndAssign, bitand_assign for Wrapping<$t>, $t } + #[stable(feature = "wrapping_neg", since = "1.10.0")] #[rustc_const_unstable(feature = "const_ops", issue = "90080")] impl const Neg for Wrapping<$t> { diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index e954742938..e367be8c16 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -65,11 +65,36 @@ /// ``` #[lang = "add"] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented( - on(all(_Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",), - on(all(_Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",), - message = "cannot add `{Rhs}` to `{Self}`", - label = "no implementation for `{Self} + {Rhs}`" +#[cfg_attr( + bootstrap, + rustc_on_unimplemented( + on( + all(_Self = "{integer}", Rhs = "{float}"), + message = "cannot add a float to an integer", + ), + on( + all(_Self = "{float}", Rhs = "{integer}"), + message = "cannot add an integer to a float", + ), + message = "cannot add `{Rhs}` to `{Self}`", + label = "no implementation for `{Self} + {Rhs}`" + ) +)] +#[cfg_attr( + not(bootstrap), + rustc_on_unimplemented( + on( + all(_Self = "{integer}", Rhs = "{float}"), + message = "cannot add a float to an integer", + ), + on( + all(_Self = "{float}", Rhs = "{integer}"), + message = "cannot add an integer to a float", + ), + message = "cannot add `{Rhs}` to `{Self}`", + label = "no implementation for `{Self} + {Rhs}`", + append_const_msg, + ) )] #[doc(alias = "+")] pub trait Add { diff --git a/library/core/src/ops/bit.rs b/library/core/src/ops/bit.rs index 255f6cb793..7c664226fc 100644 --- a/library/core/src/ops/bit.rs +++ b/library/core/src/ops/bit.rs @@ -68,6 +68,17 @@ macro_rules! not_impl { not_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } +#[stable(feature = "not_never", since = "1.60.0")] +#[rustc_const_unstable(feature = "const_ops", issue = "90080")] +impl const Not for ! { + type Output = !; + + #[inline] + fn not(self) -> ! { + match self {} + } +} + /// The bitwise AND operator `&`. /// /// Note that `Rhs` is `Self` by default, but this is not mandatory. diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 10a24a545d..e34e26746c 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -134,7 +134,6 @@ impl ControlFlow { /// # Examples /// /// ``` - /// #![feature(control_flow_enum)] /// use std::ops::ControlFlow; /// /// assert!(ControlFlow::::Break(3).is_break()); @@ -151,7 +150,6 @@ impl ControlFlow { /// # Examples /// /// ``` - /// #![feature(control_flow_enum)] /// use std::ops::ControlFlow; /// /// assert!(!ControlFlow::::Break(3).is_continue()); diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 6a414ae8c4..ba369e7f3a 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -302,6 +302,7 @@ pub trait Try: FromResidual { enclosing_scope = "this function should return `Result` or `Option` to accept `?`" ), )] +#[rustc_diagnostic_item = "FromResidual"] #[unstable(feature = "try_trait_v2", issue = "84277")] pub trait FromResidual::Residual> { /// Constructs the type from a compatible `Residual` type. @@ -359,6 +360,14 @@ pub(crate) type ChangeOutputType = <::Residual as Residual>:: #[repr(transparent)] pub(crate) struct NeverShortCircuit(pub T); +impl NeverShortCircuit { + /// Wrap a binary `FnMut` to return its result wrapped in a `NeverShortCircuit`. + #[inline] + pub fn wrap_mut_2(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self { + move |a, b| NeverShortCircuit(f(a, b)) + } +} + pub(crate) enum NeverShortCircuitResidual {} impl Try for NeverShortCircuit { diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 1ec119a71e..508837f63c 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -270,7 +270,7 @@ //! let mut bt = BTreeMap::new(); //! bt.insert(20u8, "foo"); //! bt.insert(42u8, "bar"); -//! let res = vec![0u8, 1, 11, 200, 22] +//! let res = [0u8, 1, 11, 200, 22] //! .into_iter() //! .map(|x| { //! // `checked_sub()` returns `None` on error @@ -390,10 +390,10 @@ //! [impl-FromIterator]: Option#impl-FromIterator%3COption%3CA%3E%3E //! //! ``` -//! let v = vec![Some(2), Some(4), None, Some(8)]; +//! let v = [Some(2), Some(4), None, Some(8)]; //! let res: Option> = v.into_iter().collect(); //! assert_eq!(res, None); -//! let v = vec![Some(2), Some(4), Some(8)]; +//! let v = [Some(2), Some(4), Some(8)]; //! let res: Option> = v.into_iter().collect(); //! assert_eq!(res, Some(vec![2, 4, 8])); //! ``` @@ -407,10 +407,10 @@ //! [impl-Sum]: Option#impl-Sum%3COption%3CU%3E%3E //! //! ``` -//! let v = vec![None, Some(1), Some(2), Some(3)]; +//! let v = [None, Some(1), Some(2), Some(3)]; //! let res: Option = v.into_iter().sum(); //! assert_eq!(res, None); -//! let v = vec![Some(1), Some(2), Some(21)]; +//! let v = [Some(1), Some(2), Some(21)]; //! let res: Option = v.into_iter().product(); //! assert_eq!(res, Some(42)); //! ``` @@ -500,7 +500,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::iter::{FromIterator, FusedIterator, TrustedLen}; +use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; use crate::panicking::{panic, panic_str}; use crate::pin::Pin; use crate::{ @@ -551,6 +551,29 @@ impl Option { matches!(*self, Some(_)) } + /// Returns `true` if the option is a [`Some`] wrapping a value matching the predicate. + /// + /// # Examples + /// + /// ``` + /// #![feature(is_some_with)] + /// + /// let x: Option = Some(2); + /// assert_eq!(x.is_some_with(|&x| x > 1), true); + /// + /// let x: Option = Some(0); + /// assert_eq!(x.is_some_with(|&x| x > 1), false); + /// + /// let x: Option = None; + /// assert_eq!(x.is_some_with(|&x| x > 1), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_some_with", issue = "93050")] + pub fn is_some_with(&self, f: impl FnOnce(&T) -> bool) -> bool { + matches!(self, Some(x) if f(x)) + } + /// Returns `true` if the option is a [`None`] value. /// /// # Examples @@ -1184,13 +1207,25 @@ impl Option { /// # Examples /// /// ``` - /// fn sq(x: u32) -> Option { Some(x * x) } - /// fn nope(_: u32) -> Option { None } + /// fn sq_then_to_string(x: u32) -> Option { + /// x.checked_mul(x).map(|sq| sq.to_string()) + /// } + /// + /// assert_eq!(Some(2).and_then(sq_then_to_string), Some(4.to_string())); + /// assert_eq!(Some(1_000_000).and_then(sq_then_to_string), None); // overflowed! + /// assert_eq!(None.and_then(sq_then_to_string), None); + /// ``` + /// + /// Often used to chain fallible operations that may return [`None`]. + /// + /// ``` + /// let arr_2d = [["A0", "A1"], ["B0", "B1"]]; + /// + /// let item_0_1 = arr_2d.get(0).and_then(|row| row.get(1)); + /// assert_eq!(item_0_1, Some(&"A1")); /// - /// assert_eq!(Some(2).and_then(sq).and_then(sq), Some(16)); - /// assert_eq!(Some(2).and_then(sq).and_then(nope), None); - /// assert_eq!(Some(2).and_then(nope).and_then(sq), None); - /// assert_eq!(None.and_then(sq).and_then(sq), None); + /// let item_2_0 = arr_2d.get(2).and_then(|row| row.get(0)); + /// assert_eq!(item_2_0, None); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -2210,7 +2245,7 @@ impl> FromIterator> for Option { // FIXME(#11084): This could be replaced with Iterator::scan when this // performance bug is closed. - iter.into_iter().map(|x| x.ok_or(())).collect::>().ok() + iter::try_process(iter.into_iter(), |i| i.collect()) } } diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index d8e421df5d..be8598fae0 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -31,6 +31,7 @@ pub struct PanicInfo<'a> { payload: &'a (dyn Any + Send), message: Option<&'a fmt::Arguments<'a>>, location: &'a Location<'a>, + can_unwind: bool, } impl<'a> PanicInfo<'a> { @@ -44,9 +45,10 @@ impl<'a> PanicInfo<'a> { pub fn internal_constructor( message: Option<&'a fmt::Arguments<'a>>, location: &'a Location<'a>, + can_unwind: bool, ) -> Self { struct NoPayload; - PanicInfo { location, message, payload: &NoPayload } + PanicInfo { location, message, payload: &NoPayload, can_unwind } } #[unstable( @@ -127,6 +129,22 @@ impl<'a> PanicInfo<'a> { // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. Some(&self.location) } + + /// Returns whether the panic handler is allowed to unwind the stack from + /// the point where the panic occurred. + /// + /// This is true for most kinds of panics with the exception of panics + /// caused by trying to unwind out of a `Drop` implementation or a function + /// whose ABI does not support unwinding. + /// + /// It is safe for a panic handler to unwind even when this function returns + /// true, however this will simply cause the panic handler to be called + /// again. + #[must_use] + #[unstable(feature = "panic_can_unwind", issue = "92988")] + pub fn can_unwind(&self) -> bool { + self.can_unwind + } } #[stable(feature = "panic_hook_display", since = "1.26.0")] diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs index 092b7cf0f2..95be879e31 100644 --- a/library/core/src/panic/unwind_safe.rs +++ b/library/core/src/panic/unwind_safe.rs @@ -1,10 +1,10 @@ +use crate::async_iter::AsyncIterator; use crate::cell::UnsafeCell; use crate::fmt; use crate::future::Future; use crate::ops::{Deref, DerefMut}; use crate::pin::Pin; use crate::ptr::{NonNull, Unique}; -use crate::stream::Stream; use crate::task::{Context, Poll}; /// A marker trait which represents "panic safe" types in Rust. @@ -290,8 +290,8 @@ impl Future for AssertUnwindSafe { } } -#[unstable(feature = "async_stream", issue = "79024")] -impl Stream for AssertUnwindSafe { +#[unstable(feature = "async_iterator", issue = "79024")] +impl AsyncIterator for AssertUnwindSafe { type Item = S::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 422f0e187d..0798076411 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -68,6 +68,7 @@ pub fn unreachable_display(x: &T) -> ! { #[track_caller] #[lang = "panic_display"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval +#[rustc_const_unstable(feature = "core_panic", issue = "none")] pub const fn panic_display(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } @@ -84,6 +85,31 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { panic!("index out of bounds: the len is {} but the index is {}", len, index) } +#[cfg(not(bootstrap))] +#[cold] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] +#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function +fn panic_no_unwind() -> ! { + if cfg!(feature = "panic_immediate_abort") { + super::intrinsics::abort() + } + + // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call + // that gets resolved to the `#[panic_handler]` function. + extern "Rust" { + #[lang = "panic_impl"] + fn panic_impl(pi: &PanicInfo<'_>) -> !; + } + + // PanicInfo with the `can_unwind` flag set to false forces an abort. + let fmt = format_args!("panic in a function that cannot unwind"); + let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), false); + + // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. + unsafe { panic_impl(&pi) } +} + /// The entry point for panicking with a formatted message. /// /// This is designed to reduce the amount of code required at the call @@ -98,6 +124,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { #[track_caller] #[lang = "panic_fmt"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval +#[rustc_const_unstable(feature = "core_panic", issue = "none")] pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() @@ -110,7 +137,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { fn panic_impl(pi: &PanicInfo<'_>) -> !; } - let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller()); + let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), true); // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. unsafe { panic_impl(&pi) } @@ -118,6 +145,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { /// This function is used instead of panic_fmt in const eval. #[lang = "const_panic_fmt"] +#[rustc_const_unstable(feature = "core_panic", issue = "none")] pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if let Some(msg) = fmt.as_str() { panic_str(msg); diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 09fc6df542..dec1b5270d 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -406,7 +406,14 @@ use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; #[repr(transparent)] #[derive(Copy, Clone)] pub struct Pin

{ - pointer: P, + // FIXME(#93176): this field is made `#[unstable] #[doc(hidden)] pub` to: + // - deter downstream users from accessing it (which would be unsound!), + // - let the `pin!` macro access it (such a macro requires using struct + // literal syntax in order to benefit from lifetime extension). + // Long-term, `unsafe` fields or macro hygiene are expected to offer more robust alternatives. + #[unstable(feature = "unsafe_pin_internals", issue = "none")] + #[doc(hidden)] + pub pointer: P, } // The following implementations aren't derived in order to avoid soundness @@ -909,3 +916,243 @@ impl CoerceUnsized> for Pin

where P: CoerceUnsized {} #[stable(feature = "pin", since = "1.33.0")] impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} + +/// Constructs a [Pin]<[&mut] T>, by pinning[^1] a `value: T` _locally_[^2]. +/// +/// Unlike [`Box::pin`], this does not involve a heap allocation. +/// +/// [^1]: If the (type `T` of the) given value does not implement [`Unpin`], then this +/// effectively pins the `value` in memory, where it will be unable to be moved. +/// Otherwise, [Pin]<[&mut] T> behaves like [&mut] T, and operations such +/// as [`mem::replace()`][crate::mem::replace] will allow extracting that value, and therefore, +/// moving it. +/// See [the `Unpin` section of the `pin` module][self#unpin] for more info. +/// +/// [^2]: This is usually dubbed "stack"-pinning. And whilst local values are almost always located +/// in the stack (_e.g._, when within the body of a non-`async` function), the truth is that inside +/// the body of an `async fn` or block —more generally, the body of a generator— any locals crossing +/// an `.await` point —a `yield` point— end up being part of the state captured by the `Future` —by +/// the `Generator`—, and thus will be stored wherever that one is. +/// +/// ## Examples +/// +/// ### Basic usage +/// +/// ```rust +/// #![feature(pin_macro)] +/// # use core::marker::PhantomPinned as Foo; +/// use core::pin::{pin, Pin}; +/// +/// fn stuff(foo: Pin<&mut Foo>) { +/// // … +/// # let _ = foo; +/// } +/// +/// let pinned_foo = pin!(Foo { /* … */ }); +/// stuff(pinned_foo); +/// // or, directly: +/// stuff(pin!(Foo { /* … */ })); +/// ``` +/// +/// ### Manually polling a `Future` (wihout `Unpin` bounds) +/// +/// ```rust +/// #![feature(pin_macro)] +/// use std::{ +/// future::Future, +/// pin::pin, +/// task::{Context, Poll}, +/// thread, +/// }; +/// # use std::{sync::Arc, task::Wake, thread::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(); +/// # } +/// # } +/// # +/// /// Runs a future to completion. +/// fn block_on(fut: Fut) -> Fut::Output { +/// let waker_that_unparks_thread = // … +/// # Arc::new(ThreadWaker(thread::current())).into(); +/// let mut cx = Context::from_waker(&waker_that_unparks_thread); +/// // Pin the future so it can be polled. +/// let mut pinned_fut = pin!(fut); +/// loop { +/// match pinned_fut.as_mut().poll(&mut cx) { +/// Poll::Pending => thread::park(), +/// Poll::Ready(res) => return res, +/// } +/// } +/// } +/// # +/// # assert_eq!(42, block_on(async { 42 })); +/// ``` +/// +/// ### With `Generator`s +/// +/// ```rust +/// #![feature(generators, generator_trait, pin_macro)] +/// use core::{ +/// ops::{Generator, GeneratorState}, +/// pin::pin, +/// }; +/// +/// fn generator_fn() -> impl Generator /* not Unpin */ { +/// // Allow generator to be self-referential (not `Unpin`) +/// // vvvvvv so that locals can cross yield points. +/// static || { +/// let foo = String::from("foo"); // --+ +/// yield 0; // | <- crosses yield point! +/// println!("{}", &foo); // <----------+ +/// yield foo.len(); +/// } +/// } +/// +/// fn main() { +/// let mut generator = pin!(generator_fn()); +/// match generator.as_mut().resume(()) { +/// GeneratorState::Yielded(0) => {}, +/// _ => unreachable!(), +/// } +/// match generator.as_mut().resume(()) { +/// GeneratorState::Yielded(3) => {}, +/// _ => unreachable!(), +/// } +/// match generator.resume(()) { +/// GeneratorState::Yielded(_) => unreachable!(), +/// GeneratorState::Complete(()) => {}, +/// } +/// } +/// ``` +/// +/// ## Remarks +/// +/// Precisely because a value is pinned to local storage, the resulting [Pin]<[&mut] T> +/// reference ends up borrowing a local tied to that block: it can't escape it. +/// +/// The following, for instance, fails to compile: +/// +/// ```rust,compile_fail +/// #![feature(pin_macro)] +/// use core::pin::{pin, Pin}; +/// # use core::{marker::PhantomPinned as Foo, mem::drop as stuff}; +/// +/// let x: Pin<&mut Foo> = { +/// let x: Pin<&mut Foo> = pin!(Foo { /* … */ }); +/// x +/// }; // <- Foo is dropped +/// stuff(x); // Error: use of dropped value +/// ``` +/// +///

Error message +/// +/// ```console +/// error[E0716]: temporary value dropped while borrowed +/// --> src/main.rs:9:28 +/// | +/// 8 | let x: Pin<&mut Foo> = { +/// | - borrow later stored here +/// 9 | let x: Pin<&mut Foo> = pin!(Foo { /* … */ }); +/// | ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use +/// 10 | x +/// 11 | }; // <- Foo is dropped +/// | - temporary value is freed at the end of this statement +/// | +/// = note: consider using a `let` binding to create a longer lived value +/// ``` +/// +///
+/// +/// This makes [`pin!`] **unsuitable to pin values when intending to _return_ them**. Instead, the +/// value is expected to be passed around _unpinned_ until the point where it is to be consumed, +/// where it is then useful and even sensible to pin the value locally using [`pin!`]. +/// +/// If you really need to return a pinned value, consider using [`Box::pin`] instead. +/// +/// On the other hand, pinning to the stack[2](#fn2) using [`pin!`] is likely to be +/// cheaper than pinning into a fresh heap allocation using [`Box::pin`]. Moreover, by virtue of not +/// even needing an allocator, [`pin!`] is the main non-`unsafe` `#![no_std]`-compatible [`Pin`] +/// constructor. +/// +/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin +#[unstable(feature = "pin_macro", issue = "93178")] +#[rustc_macro_transparency = "semitransparent"] +#[allow_internal_unstable(unsafe_pin_internals)] +pub macro pin($value:expr $(,)?) { + // This is `Pin::new_unchecked(&mut { $value })`, so, for starters, let's + // review such a hypothetical macro (that any user-code could define): + // + // ```rust + // macro_rules! pin {( $value:expr ) => ( + // match &mut { $value } { at_value => unsafe { // Do not wrap `$value` in an `unsafe` block. + // $crate::pin::Pin::<&mut _>::new_unchecked(at_value) + // }} + // )} + // ``` + // + // Safety: + // - `type P = &mut _`. There are thus no pathological `Deref{,Mut}` impls + // that would break `Pin`'s invariants. + // - `{ $value }` is braced, making it a _block expression_, thus **moving** + // the given `$value`, and making it _become an **anonymous** temporary_. + // By virtue of being anonynomous, it can no longer be accessed, thus + // preventing any attemps to `mem::replace` it or `mem::forget` it, _etc._ + // + // This gives us a `pin!` definition that is sound, and which works, but only + // in certain scenarios: + // - If the `pin!(value)` expression is _directly_ fed to a function call: + // `let poll = pin!(fut).poll(cx);` + // - If the `pin!(value)` expression is part of a scrutinee: + // ```rust + // match pin!(fut) { pinned_fut => { + // pinned_fut.as_mut().poll(...); + // pinned_fut.as_mut().poll(...); + // }} // <- `fut` is dropped here. + // ``` + // Alas, it doesn't work for the more straight-forward use-case: `let` bindings. + // ```rust + // let pinned_fut = pin!(fut); // <- temporary value is freed at the end of this statement + // pinned_fut.poll(...) // error[E0716]: temporary value dropped while borrowed + // // note: consider using a `let` binding to create a longer lived value + // ``` + // - Issues such as this one are the ones motivating https://github.com/rust-lang/rfcs/pull/66 + // + // This makes such a macro incredibly unergonomic in practice, and the reason most macros + // out there had to take the path of being a statement/binding macro (_e.g._, `pin!(future);`) + // instead of featuring the more intuitive ergonomics of an expression macro. + // + // Luckily, there is a way to avoid the problem. Indeed, the problem stems from the fact that a + // temporary is dropped at the end of its enclosing statement when it is part of the parameters + // given to function call, which has precisely been the case with our `Pin::new_unchecked()`! + // For instance, + // ```rust + // let p = Pin::new_unchecked(&mut ); + // ``` + // becomes: + // ```rust + // let p = { let mut anon = ; &mut anon }; + // ``` + // + // However, when using a literal braced struct to construct the value, references to temporaries + // can then be taken. This makes Rust change the lifespan of such temporaries so that they are, + // instead, dropped _at the end of the enscoping block_. + // For instance, + // ```rust + // let p = Pin { pointer: &mut }; + // ``` + // becomes: + // ```rust + // let mut anon = ; + // let p = Pin { pointer: &mut anon }; + // ``` + // which is *exactly* what we want. + // + // See https://doc.rust-lang.org/1.58.1/reference/destructors.html#temporary-lifetime-extension + // for more info. + $crate::pin::Pin::<&mut _> { pointer: &mut { $value } } +} diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 0fb8846288..b566e211cd 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -56,8 +56,8 @@ pub use crate::hash::macros::Hash; #[doc(no_inline)] pub use crate::{ assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, llvm_asm, log_syntax, module_path, - option_env, stringify, trace_macros, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, }; #[unstable( @@ -65,7 +65,6 @@ pub use crate::{ issue = "87555", reason = "`concat_bytes` is not stable enough for use and is subject to change" )] -#[cfg(not(bootstrap))] #[doc(no_inline)] pub use crate::concat_bytes; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 8fcd8cdeb1..ebb1d8971b 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -275,20 +275,69 @@ mod prim_bool {} mod prim_never {} #[doc(primitive = "char")] +#[allow(rustdoc::invalid_rust_codeblocks)] /// A character type. /// /// The `char` type represents a single character. More specifically, since /// 'character' isn't a well-defined concept in Unicode, `char` is a '[Unicode -/// scalar value]', which is similar to, but not the same as, a '[Unicode code -/// point]'. -/// -/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value -/// [Unicode code point]: https://www.unicode.org/glossary/#code_point +/// scalar value]'. /// /// This documentation describes a number of methods and trait implementations on the /// `char` type. For technical reasons, there is additional, separate /// documentation in [the `std::char` module](char/index.html) as well. /// +/// # Validity +/// +/// A `char` is a '[Unicode scalar value]', which is any '[Unicode code point]' +/// other than a [surrogate code point]. This has a fixed numerical definition: +/// code points are in the range 0 to 0x10FFFF, inclusive. +/// Surrogate code points, used by UTF-16, are in the range 0xD800 to 0xDFFF. +/// +/// No `char` may be constructed, whether as a literal or at runtime, that is not a +/// Unicode scalar value: +/// +/// ```compile_fail +/// // Each of these is a compiler error +/// ['\u{D800}', '\u{DFFF}', '\u{110000}']; +/// ``` +/// +/// ```should_panic +/// // Panics; from_u32 returns None. +/// char::from_u32(0xDE01).unwrap(); +/// ``` +/// +/// ```no_run +/// // Undefined behaviour +/// unsafe { char::from_u32_unchecked(0x110000) }; +/// ``` +/// +/// USVs are also the exact set of values that may be encoded in UTF-8. Because +/// `char` values are USVs and `str` values are valid UTF-8, it is safe to store +/// any `char` in a `str` or read any character from a `str` as a `char`. +/// +/// The gap in valid `char` values is understood by the compiler, so in the +/// below example the two ranges are understood to cover the whole range of +/// possible `char` values and there is no error for a [non-exhaustive match]. +/// +/// ``` +/// let c: char = 'a'; +/// match c { +/// '\0' ..= '\u{D7FF}' => false, +/// '\u{E000}' ..= '\u{10FFFF}' => true, +/// }; +/// ``` +/// +/// All USVs are valid `char` values, but not all of them represent a real +/// character. Many USVs are not currently assigned to a character, but may be +/// in the future ("reserved"); some will never be a character +/// ("noncharacters"); and some may be given different meanings by different +/// users ("private use"). +/// +/// [Unicode code point]: https://www.unicode.org/glossary/#code_point +/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value +/// [non-exhaustive match]: ../book/ch06-02-match.html#matches-are-exhaustive +/// [surrogate code point]: https://www.unicode.org/glossary/#surrogate_code_point +/// /// # Representation /// /// `char` is always four bytes in size. This is a different representation than diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index a93327a013..485a5965f4 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -48,6 +48,16 @@ impl *const T { self as _ } + /// Changes constness without changing the type. + /// + /// This is a bit safer than `as` because it wouldn't silently change the type if the code is + /// refactored. + #[unstable(feature = "ptr_const_cast", issue = "92675")] + #[rustc_const_unstable(feature = "ptr_const_cast", issue = "92675")] + pub const fn as_mut(self) -> *mut T { + self as _ + } + /// Casts a pointer to its raw bits. /// /// This is equivalent to `as usize`, but is more specific to enhance readability. @@ -429,7 +439,7 @@ impl *const T { /// } /// ``` #[stable(feature = "ptr_offset_from", since = "1.47.0")] - #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")] #[inline] pub const unsafe fn offset_from(self, origin: *const T) -> isize where diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5fd3b2ebc6..1412e836eb 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -47,6 +47,20 @@ impl *mut T { self as _ } + /// Changes constness without changing the type. + /// + /// This is a bit safer than `as` because it wouldn't silently change the type if the code is + /// refactored. + /// + /// While not strictly required (`*mut T` coerces to `*const T`), this is provided for symmetry + /// with `as_mut()` on `*const T` and may have documentation value if used instead of implicit + /// coercion. + #[unstable(feature = "ptr_const_cast", issue = "92675")] + #[rustc_const_unstable(feature = "ptr_const_cast", issue = "92675")] + pub const fn as_const(self) -> *const T { + self as _ + } + /// Casts a pointer to its raw bits. /// /// This is equivalent to `as usize`, but is more specific to enhance readability. @@ -603,7 +617,7 @@ impl *mut T { /// } /// ``` #[stable(feature = "ptr_offset_from", since = "1.47.0")] - #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")] #[inline(always)] pub const unsafe fn offset_from(self, origin: *const T) -> isize where diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 3a7e99facc..0aa8e9960a 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -211,8 +211,9 @@ impl NonNull { /// } /// ``` #[stable(feature = "nonnull", since = "1.25.0")] + #[rustc_const_unstable(feature = "const_nonnull_new", issue = "93235")] #[inline] - pub fn new(ptr: *mut T) -> Option { + pub const fn new(ptr: *mut T) -> Option { if !ptr.is_null() { // SAFETY: The pointer is already checked and is not null Some(unsafe { Self::new_unchecked(ptr) }) @@ -720,6 +721,9 @@ impl const From> for NonNull { #[stable(feature = "nonnull", since = "1.25.0")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] impl const From<&mut T> for NonNull { + /// Converts a `&mut T` to a `NonNull`. + /// + /// This conversion is safe and infallible since references cannot be null. #[inline] fn from(reference: &mut T) -> Self { // SAFETY: A mutable reference cannot be null. @@ -730,6 +734,9 @@ impl const From<&mut T> for NonNull { #[stable(feature = "nonnull", since = "1.25.0")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] impl const From<&T> for NonNull { + /// Converts a `&T` to a `NonNull`. + /// + /// This conversion is safe and infallible since references cannot be null. #[inline] fn from(reference: &T) -> Self { // SAFETY: A reference cannot be null, so the conditions for diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index f5c624c225..661d111c99 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -178,6 +178,9 @@ impl fmt::Pointer for Unique { #[unstable(feature = "ptr_internals", issue = "none")] impl const From<&mut T> for Unique { + /// Converts a `&mut T` to a `Unique`. + /// + /// This conversion is infallible since references cannot be null. #[inline] fn from(reference: &mut T) -> Self { // SAFETY: A mutable reference cannot be null diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 575fd2b42d..801e3a0b3a 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -436,7 +436,7 @@ //! # use std::str::FromStr; //! let mut results = vec![]; //! let mut errs = vec![]; -//! let nums: Vec<_> = vec!["17", "not a number", "99", "-27", "768"] +//! let nums: Vec<_> = ["17", "not a number", "99", "-27", "768"] //! .into_iter() //! .map(u8::from_str) //! // Save clones of the raw `Result` values to inspect @@ -462,10 +462,10 @@ //! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA%2C%20E%3E%3E //! //! ``` -//! let v = vec![Ok(2), Ok(4), Err("err!"), Ok(8)]; +//! let v = [Ok(2), Ok(4), Err("err!"), Ok(8)]; //! let res: Result, &str> = v.into_iter().collect(); //! assert_eq!(res, Err("err!")); -//! let v = vec![Ok(2), Ok(4), Ok(8)]; +//! let v = [Ok(2), Ok(4), Ok(8)]; //! let res: Result, &str> = v.into_iter().collect(); //! assert_eq!(res, Ok(vec![2, 4, 8])); //! ``` @@ -479,10 +479,10 @@ //! [impl-Sum]: Result#impl-Sum%3CResult%3CU%2C%20E%3E%3E //! //! ``` -//! let v = vec![Err("error!"), Ok(1), Ok(2), Ok(3), Err("foo")]; +//! let v = [Err("error!"), Ok(1), Ok(2), Ok(3), Err("foo")]; //! let res: Result = v.into_iter().sum(); //! assert_eq!(res, Err("error!")); -//! let v: Vec> = vec![Ok(1), Ok(2), Ok(21)]; +//! let v = [Ok(1), Ok(2), Ok(21)]; //! let res: Result = v.into_iter().product(); //! assert_eq!(res, Ok(42)); //! ``` @@ -542,6 +542,29 @@ impl Result { matches!(*self, Ok(_)) } + /// Returns `true` if the result is [`Ok`] wrapping a value matching the predicate. + /// + /// # Examples + /// + /// ``` + /// #![feature(is_some_with)] + /// + /// let x: Result = Ok(2); + /// assert_eq!(x.is_ok_with(|&x| x > 1), true); + /// + /// let x: Result = Ok(0); + /// assert_eq!(x.is_ok_with(|&x| x > 1), false); + /// + /// let x: Result = Err("hey"); + /// assert_eq!(x.is_ok_with(|&x| x > 1), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_some_with", issue = "93050")] + pub fn is_ok_with(&self, f: impl FnOnce(&T) -> bool) -> bool { + matches!(self, Ok(x) if f(x)) + } + /// Returns `true` if the result is [`Err`]. /// /// # Examples @@ -563,6 +586,30 @@ impl Result { !self.is_ok() } + /// Returns `true` if the result is [`Err`] wrapping a value matching the predicate. + /// + /// # Examples + /// + /// ``` + /// #![feature(is_some_with)] + /// use std::io::{Error, ErrorKind}; + /// + /// let x: Result = Err(Error::new(ErrorKind::NotFound, "!")); + /// assert_eq!(x.is_err_with(|x| x.kind() == ErrorKind::NotFound), true); + /// + /// let x: Result = Err(Error::new(ErrorKind::PermissionDenied, "!")); + /// assert_eq!(x.is_err_with(|x| x.kind() == ErrorKind::NotFound), false); + /// + /// let x: Result = Ok(123); + /// assert_eq!(x.is_err_with(|x| x.kind() == ErrorKind::NotFound), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_some_with", issue = "93050")] + pub fn is_err_with(&self, f: impl FnOnce(&E) -> bool) -> bool { + matches!(self, Err(x) if f(x)) + } + ///////////////////////////////////////////////////////////////////////// // Adapter for each variant ///////////////////////////////////////////////////////////////////////// @@ -1234,16 +1281,28 @@ impl Result { /// /// # Examples /// - /// Basic usage: + /// ``` + /// fn sq_then_to_string(x: u32) -> Result { + /// x.checked_mul(x).map(|sq| sq.to_string()).ok_or("overflowed") + /// } /// + /// assert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string())); + /// assert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err("overflowed")); + /// assert_eq!(Err("not a number").and_then(sq_then_to_string), Err("not a number")); /// ``` - /// fn sq(x: u32) -> Result { Ok(x * x) } - /// fn err(x: u32) -> Result { Err(x) } /// - /// assert_eq!(Ok(2).and_then(sq).and_then(sq), Ok(16)); - /// assert_eq!(Ok(2).and_then(sq).and_then(err), Err(4)); - /// assert_eq!(Ok(2).and_then(err).and_then(sq), Err(2)); - /// assert_eq!(Err(3).and_then(sq).and_then(sq), Err(3)); + /// Often used to chain fallible operations that may return [`Err`]. + /// + /// ``` + /// use std::{io::ErrorKind, path::Path}; + /// + /// // Note: on Windows "/" maps to "C:\" + /// let root_modified_time = Path::new("/").metadata().and_then(|md| md.modified()); + /// assert!(root_modified_time.is_ok()); + /// + /// let should_fail = Path::new("/bad/path").metadata().and_then(|md| md.modified()); + /// assert!(should_fail.is_err()); + /// assert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -1969,7 +2028,7 @@ impl> FromIterator> for Result { // FIXME(#11084): This could be replaced with Iterator::scan when this // performance bug is closed. - iter::process_results(iter.into_iter(), |i| i.collect()) + iter::try_process(iter.into_iter(), |i| i.collect()) } } diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 080256f493..304ba7ee55 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -68,7 +68,6 @@ impl [u8] { /// # Examples /// /// ``` - /// #![feature(inherent_ascii_escape)] /// /// let s = b"0\t\r\n'\"\\\x9d"; /// let escaped = s.escape_ascii().to_string(); @@ -76,7 +75,7 @@ impl [u8] { /// ``` #[must_use = "this returns the escaped bytes as an iterator, \ without modifying the original"] - #[unstable(feature = "inherent_ascii_escape", issue = "77174")] + #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub fn escape_ascii(&self) -> EscapeAscii<'_> { EscapeAscii { inner: self.iter().flat_map(EscapeByte) } } @@ -93,13 +92,13 @@ impl_fn_for_zst! { /// /// This `struct` is created by the [`slice::escape_ascii`] method. See its /// documentation for more information. -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] #[derive(Clone)] pub struct EscapeAscii<'a> { inner: iter::FlatMap, ascii::EscapeDefault, EscapeByte>, } -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] impl<'a> iter::Iterator for EscapeAscii<'a> { type Item = u8; #[inline] @@ -131,23 +130,23 @@ impl<'a> iter::Iterator for EscapeAscii<'a> { } } -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] impl<'a> iter::DoubleEndedIterator for EscapeAscii<'a> { fn next_back(&mut self) -> Option { self.inner.next_back() } } -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] impl<'a> iter::ExactSizeIterator for EscapeAscii<'a> {} -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] impl<'a> iter::FusedIterator for EscapeAscii<'a> {} -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] impl<'a> fmt::Display for EscapeAscii<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.clone().try_for_each(|b| f.write_char(b as char)) } } -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] impl<'a> fmt::Debug for EscapeAscii<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("EscapeAscii").finish_non_exhaustive() diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 72af47c71d..27c6b6f5bc 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -1,7 +1,6 @@ //! Comparison traits for `[T]`. -use crate::cmp; -use crate::cmp::Ordering::{self, Greater, Less}; +use crate::cmp::{self, Ordering}; use crate::mem; use super::from_raw_parts; @@ -189,18 +188,18 @@ impl SliceOrd for A { impl SliceOrd for u8 { #[inline] fn compare(left: &[Self], right: &[Self]) -> Ordering { - let order = - // SAFETY: `left` and `right` are references and are thus guaranteed to be valid. - // We use the minimum of both lengths which guarantees that both regions are - // valid for reads in that interval. - unsafe { memcmp(left.as_ptr(), right.as_ptr(), cmp::min(left.len(), right.len())) }; + // Since the length of a slice is always less than or equal to isize::MAX, this never underflows. + let diff = left.len() as isize - right.len() as isize; + // This comparison gets optimized away (on x86_64 and ARM) because the subtraction updates flags. + let len = if left.len() < right.len() { left.len() } else { right.len() }; + // SAFETY: `left` and `right` are references and are thus guaranteed to be valid. + // We use the minimum of both lengths which guarantees that both regions are + // valid for reads in that interval. + let mut order = unsafe { memcmp(left.as_ptr(), right.as_ptr(), len) as isize }; if order == 0 { - left.len().cmp(&right.len()) - } else if order < 0 { - Less - } else { - Greater + order = diff; } + order.cmp(&0) } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 176820efe3..d260cc6946 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1476,7 +1476,21 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { } else { let remainder = self.v.len() % self.chunk_size; let chunksz = if remainder != 0 { remainder } else { self.chunk_size }; - let (fst, snd) = self.v.split_at(self.v.len() - chunksz); + // SAFETY: split_at_unchecked requires the argument be less than or + // equal to the length. This is guaranteed, but subtle: `chunksz` + // will always either be `self.v.len() % self.chunk_size`, which + // will always evaluate to strictly less than `self.v.len()` (or + // panic, in the case that `self.chunk_size` is zero), or it can be + // `self.chunk_size`, in the case that the length is exactly + // divisible by the chunk size. + // + // While it seems like using `self.chunk_size` in this case could + // lead to a value greater than `self.v.len()`, it cannot: if + // `self.chunk_size` were greater than `self.v.len()`, then + // `self.v.len() % self.chunk_size` would return nonzero (note that + // in this branch of the `if`, we already know that `self.v` is + // non-empty). + let (fst, snd) = unsafe { self.v.split_at_unchecked(self.v.len() - chunksz) }; self.v = fst; Some(snd) } @@ -1641,7 +1655,8 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { let sz = if remainder != 0 { remainder } else { self.chunk_size }; let tmp = mem::replace(&mut self.v, &mut []); let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - sz); + // SAFETY: Similar to `Chunks::next_back` + let (head, tail) = unsafe { tmp.split_at_mut_unchecked(tmp_len - sz) }; self.v = head; Some(tail) } @@ -2410,8 +2425,14 @@ impl<'a, T> Iterator for RChunks<'a, T> { if self.v.is_empty() { None } else { - let chunksz = cmp::min(self.v.len(), self.chunk_size); - let (fst, snd) = self.v.split_at(self.v.len() - chunksz); + let len = self.v.len(); + let chunksz = cmp::min(len, self.chunk_size); + // SAFETY: split_at_unchecked just requires the argument be less + // than the length. This could only happen if the expression `len - + // chunksz` overflows. This could only happen if `chunksz > len`, + // which is impossible as we initialize it as the `min` of `len` and + // `self.chunk_size`. + let (fst, snd) = unsafe { self.v.split_at_unchecked(len - chunksz) }; self.v = fst; Some(snd) } @@ -2485,7 +2506,8 @@ impl<'a, T> DoubleEndedIterator for RChunks<'a, T> { } else { let remainder = self.v.len() % self.chunk_size; let chunksz = if remainder != 0 { remainder } else { self.chunk_size }; - let (fst, snd) = self.v.split_at(chunksz); + // SAFETY: similar to Chunks::next_back + let (fst, snd) = unsafe { self.v.split_at_unchecked(chunksz) }; self.v = snd; Some(fst) } @@ -2571,7 +2593,12 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { let sz = cmp::min(self.v.len(), self.chunk_size); let tmp = mem::replace(&mut self.v, &mut []); let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - sz); + // SAFETY: split_at_mut_unchecked just requires the argument be less + // than the length. This could only happen if the expression + // `tmp_len - sz` overflows. This could only happen if `sz > + // tmp_len`, which is impossible as we initialize it as the `min` of + // `self.v.len()` (e.g. `tmp_len`) and `self.chunk_size`. + let (head, tail) = unsafe { tmp.split_at_mut_unchecked(tmp_len - sz) }; self.v = head; Some(tail) } @@ -2649,7 +2676,8 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { let remainder = self.v.len() % self.chunk_size; let sz = if remainder != 0 { remainder } else { self.chunk_size }; let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(sz); + // SAFETY: Similar to `Chunks::next_back` + let (head, tail) = unsafe { tmp.split_at_mut_unchecked(sz) }; self.v = tail; Some(head) } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 0599f27401..cd38c3a754 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -81,7 +81,7 @@ pub use index::SliceIndex; #[unstable(feature = "slice_range", issue = "76393")] pub use index::range; -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub use ascii::EscapeAscii; /// Calculates the direction and split point of a one-sided range. @@ -2558,50 +2558,6 @@ impl [T] { sort::quicksort(self, |a, b| f(a).lt(&f(b))); } - /// Reorder the slice such that the element at `index` is at its final sorted position. - #[unstable(feature = "slice_partition_at_index", issue = "55300")] - #[rustc_deprecated(since = "1.49.0", reason = "use the select_nth_unstable() instead")] - #[inline] - pub fn partition_at_index(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) - where - T: Ord, - { - self.select_nth_unstable(index) - } - - /// Reorder the slice with a comparator function such that the element at `index` is at its - /// final sorted position. - #[unstable(feature = "slice_partition_at_index", issue = "55300")] - #[rustc_deprecated(since = "1.49.0", reason = "use select_nth_unstable_by() instead")] - #[inline] - pub fn partition_at_index_by( - &mut self, - index: usize, - compare: F, - ) -> (&mut [T], &mut T, &mut [T]) - where - F: FnMut(&T, &T) -> Ordering, - { - self.select_nth_unstable_by(index, compare) - } - - /// Reorder the slice with a key extraction function such that the element at `index` is at its - /// final sorted position. - #[unstable(feature = "slice_partition_at_index", issue = "55300")] - #[rustc_deprecated(since = "1.49.0", reason = "use the select_nth_unstable_by_key() instead")] - #[inline] - pub fn partition_at_index_by_key( - &mut self, - index: usize, - f: F, - ) -> (&mut [T], &mut T, &mut [T]) - where - F: FnMut(&T) -> K, - K: Ord, - { - self.select_nth_unstable_by_key(index, f) - } - /// Reorder the slice such that the element at `index` is at its final sorted position. /// /// This reordering has the additional property that any value at position `i < index` will be diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index 8f58e8897b..2ba0e5320d 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -773,7 +773,7 @@ where let mid = partition_equal(v, pivot, is_less); // Continue sorting elements greater than the pivot. - v = &mut { v }[mid..]; + v = &mut v[mid..]; continue; } } @@ -784,7 +784,7 @@ where was_partitioned = was_p; // Split the slice into `left`, `pivot`, and `right`. - let (left, right) = { v }.split_at_mut(mid); + let (left, right) = v.split_at_mut(mid); let (pivot, right) = right.split_at_mut(1); let pivot = &pivot[0]; @@ -860,7 +860,7 @@ fn partition_at_index_loop<'a, T, F>( let (mid, _) = partition(v, pivot, is_less); // Split the slice into `left`, `pivot`, and `right`. - let (left, right) = { v }.split_at_mut(mid); + let (left, right) = v.split_at_mut(mid); let (pivot, right) = right.split_at_mut(1); let pivot = &pivot[0]; diff --git a/library/core/src/str/count.rs b/library/core/src/str/count.rs new file mode 100644 index 0000000000..5abc2b34c0 --- /dev/null +++ b/library/core/src/str/count.rs @@ -0,0 +1,136 @@ +//! Code for efficiently counting the number of `char`s in a UTF-8 encoded +//! string. +//! +//! Broadly, UTF-8 encodes `char`s as a "leading" byte which begins the `char`, +//! followed by some number (possibly 0) of continuation bytes. +//! +//! The leading byte can have a number of bit-patterns (with the specific +//! pattern indicating how many continuation bytes follow), but the continuation +//! bytes are always in the format `0b10XX_XXXX` (where the `X`s can take any +//! value). That is, the most significant bit is set, and the second most +//! significant bit is unset. +//! +//! To count the number of characters, we can just count the number of bytes in +//! the string which are not continuation bytes, which can be done many bytes at +//! a time fairly easily. +//! +//! Note: Because the term "leading byte" can sometimes be ambiguous (for +//! example, it could also refer to the first byte of a slice), we'll often use +//! the term "non-continuation byte" to refer to these bytes in the code. +use core::intrinsics::unlikely; + +const USIZE_SIZE: usize = core::mem::size_of::(); +const UNROLL_INNER: usize = 4; + +#[inline] +pub(super) fn count_chars(s: &str) -> usize { + if s.len() < USIZE_SIZE * UNROLL_INNER { + // Avoid entering the optimized implementation for strings where the + // difference is not likely to matter, or where it might even be slower. + // That said, a ton of thought was not spent on the particular threshold + // here, beyond "this value seems to make sense". + char_count_general_case(s.as_bytes()) + } else { + do_count_chars(s) + } +} + +fn do_count_chars(s: &str) -> usize { + // For correctness, `CHUNK_SIZE` must be: + // + // - Less than or equal to 255, otherwise we'll overflow bytes in `counts`. + // - A multiple of `UNROLL_INNER`, otherwise our `break` inside the + // `body.chunks(CHUNK_SIZE)` loop is incorrect. + // + // For performance, `CHUNK_SIZE` should be: + // - Relatively cheap to `/` against (so some simple sum of powers of two). + // - Large enough to avoid paying for the cost of the `sum_bytes_in_usize` + // too often. + const CHUNK_SIZE: usize = 192; + + // Check the properties of `CHUNK_SIZE` and `UNROLL_INNER` that are required + // for correctness. + const _: () = assert!(CHUNK_SIZE < 256); + const _: () = assert!(CHUNK_SIZE % UNROLL_INNER == 0); + + // SAFETY: transmuting `[u8]` to `[usize]` is safe except for size + // differences which are handled by `align_to`. + let (head, body, tail) = unsafe { s.as_bytes().align_to::() }; + + // This should be quite rare, and basically exists to handle the degenerate + // cases where align_to fails (as well as miri under symbolic alignment + // mode). + // + // The `unlikely` helps discourage LLVM from inlining the body, which is + // nice, as we would rather not mark the `char_count_general_case` function + // as cold. + if unlikely(body.is_empty() || head.len() > USIZE_SIZE || tail.len() > USIZE_SIZE) { + return char_count_general_case(s.as_bytes()); + } + + let mut total = char_count_general_case(head) + char_count_general_case(tail); + // Split `body` into `CHUNK_SIZE` chunks to reduce the frequency with which + // we call `sum_bytes_in_usize`. + for chunk in body.chunks(CHUNK_SIZE) { + // We accumulate intermediate sums in `counts`, where each byte contains + // a subset of the sum of this chunk, like a `[u8; size_of::()]`. + let mut counts = 0; + + let (unrolled_chunks, remainder) = chunk.as_chunks::(); + for unrolled in unrolled_chunks { + for &word in unrolled { + // Because `CHUNK_SIZE` is < 256, this addition can't cause the + // count in any of the bytes to overflow into a subsequent byte. + counts += contains_non_continuation_byte(word); + } + } + + // Sum the values in `counts` (which, again, is conceptually a `[u8; + // size_of::()]`), and accumulate the result into `total`. + total += sum_bytes_in_usize(counts); + + // If there's any data in `remainder`, then handle it. This will only + // happen for the last `chunk` in `body.chunks()` (because `CHUNK_SIZE` + // is divisible by `UNROLL_INNER`), so we explicitly break at the end + // (which seems to help LLVM out). + if !remainder.is_empty() { + // Accumulate all the data in the remainder. + let mut counts = 0; + for &word in remainder { + counts += contains_non_continuation_byte(word); + } + total += sum_bytes_in_usize(counts); + break; + } + } + total +} + +// Checks each byte of `w` to see if it contains the first byte in a UTF-8 +// sequence. Bytes in `w` which are continuation bytes are left as `0x00` (e.g. +// false), and bytes which are non-continuation bytes are left as `0x01` (e.g. +// true) +#[inline] +fn contains_non_continuation_byte(w: usize) -> usize { + const LSB: usize = 0x0101_0101_0101_0101u64 as usize; + ((!w >> 7) | (w >> 6)) & LSB +} + +// Morally equivalent to `values.to_ne_bytes().into_iter().sum::()`, but +// more efficient. +#[inline] +fn sum_bytes_in_usize(values: usize) -> usize { + const LSB_SHORTS: usize = 0x0001_0001_0001_0001_u64 as usize; + const SKIP_BYTES: usize = 0x00ff_00ff_00ff_00ff_u64 as usize; + + let pair_sum: usize = (values & SKIP_BYTES) + ((values >> 8) & SKIP_BYTES); + pair_sum.wrapping_mul(LSB_SHORTS) >> ((USIZE_SIZE - 2) * 8) +} + +// This is the most direct implementation of the concept of "count the number of +// bytes in the string which are not continuation bytes", and is used for the +// head and tail of the input string (the first and last item in the tuple +// returned by `slice::align_to`). +fn char_count_general_case(s: &[u8]) -> usize { + s.iter().filter(|&&byte| !super::validations::utf8_is_cont_byte(byte)).count() +} diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index de6e6d52b3..e529bccbc7 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -12,7 +12,7 @@ use crate::slice::{self, Split as SliceSplit}; use super::from_utf8_unchecked; use super::pattern::Pattern; use super::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; -use super::validations::{next_code_point, next_code_point_reverse, utf8_is_cont_byte}; +use super::validations::{next_code_point, next_code_point_reverse}; use super::LinesAnyMap; use super::{BytesIsNotEmpty, UnsafeBytesToStr}; use super::{CharEscapeDebugContinue, CharEscapeDefault, CharEscapeUnicode}; @@ -46,8 +46,7 @@ impl<'a> Iterator for Chars<'a> { #[inline] fn count(self) -> usize { - // length in `char` is equal to the number of non-continuation bytes - self.iter.filter(|&&byte| !utf8_is_cont_byte(byte)).count() + super::count::count_chars(self.as_str()) } #[inline] diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 1d4600fa4a..09709dc3cf 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -7,6 +7,7 @@ #![stable(feature = "rust1", since = "1.0.0")] mod converts; +mod count; mod error; mod iter; mod traits; @@ -75,15 +76,14 @@ use iter::MatchIndicesInternal; use iter::SplitInternal; use iter::{MatchesInternal, SplitNInternal}; -use validations::truncate_to_char_boundary; - #[inline(never)] #[cold] #[track_caller] fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { const MAX_DISPLAY_LENGTH: usize = 256; - let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH); - let ellipsis = if truncated { "[...]" } else { "" }; + let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH); + let s_trunc = &s[..trunc_len]; + let ellipsis = if trunc_len < s.len() { "[...]" } else { "" }; // 1. out of bounds if begin > s.len() || end > s.len() { @@ -104,10 +104,7 @@ fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { // 3. character boundary let index = if !s.is_char_boundary(begin) { begin } else { end }; // find the character - let mut char_start = index; - while !s.is_char_boundary(char_start) { - char_start -= 1; - } + let char_start = s.floor_char_boundary(index); // `char_start` must be less than len and a char boundary let ch = s[char_start..].chars().next().unwrap(); let char_range = char_start..char_start + ch.len_utf8(); @@ -214,8 +211,80 @@ impl str { // code on higher opt-levels. See PR #84751 for more details. None => index == self.len(), - // This is bit magic equivalent to: b < 128 || b >= 192 - Some(&b) => (b as i8) >= -0x40, + Some(&b) => b.is_utf8_char_boundary(), + } + } + + /// Finds the closest `x` not exceeding `index` where `is_char_boundary(x)` is `true`. + /// + /// This method can help you truncate a string so that it's still valid UTF-8, but doesn't + /// exceed a given number of bytes. Note that this is done purely at the character level + /// and can still visually split graphemes, even though the underlying characters aren't + /// split. For example, the emoji 🧑‍🔬 (scientist) could be split so that the string only + /// includes 🧑 (person) instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(round_char_boundary)] + /// let s = "❤️🧡💛💚💙💜"; + /// assert_eq!(s.len(), 26); + /// assert!(!s.is_char_boundary(13)); + /// + /// let closest = s.floor_char_boundary(13); + /// assert_eq!(closest, 10); + /// assert_eq!(&s[..closest], "❤️🧡"); + /// ``` + #[unstable(feature = "round_char_boundary", issue = "93743")] + #[inline] + pub fn floor_char_boundary(&self, index: usize) -> usize { + if index >= self.len() { + self.len() + } else { + let lower_bound = index.saturating_sub(3); + let new_index = self.as_bytes()[lower_bound..=index] + .iter() + .rposition(|b| b.is_utf8_char_boundary()); + + // SAFETY: we know that the character boundary will be within four bytes + unsafe { lower_bound + new_index.unwrap_unchecked() } + } + } + + /// Finds the closest `x` not below `index` where `is_char_boundary(x)` is `true`. + /// + /// This method is the natural complement to [`floor_char_boundary`]. See that method + /// for more details. + /// + /// [`floor_char_boundary`]: str::floor_char_boundary + /// + /// # Panics + /// + /// Panics if `index > self.len()`. + /// + /// # Examples + /// + /// ``` + /// #![feature(round_char_boundary)] + /// let s = "❤️🧡💛💚💙💜"; + /// assert_eq!(s.len(), 26); + /// assert!(!s.is_char_boundary(13)); + /// + /// let closest = s.ceil_char_boundary(13); + /// assert_eq!(closest, 14); + /// assert_eq!(&s[..closest], "❤️🧡💛"); + /// ``` + #[unstable(feature = "round_char_boundary", issue = "93743")] + #[inline] + pub fn ceil_char_boundary(&self, index: usize) -> usize { + if index > self.len() { + slice_error_fail(self, index, index) + } else { + let upper_bound = Ord::min(index + 4, self.len()); + self.as_bytes()[index..upper_bound] + .iter() + .position(|b| b.is_utf8_char_boundary()) + .map_or(upper_bound, |pos| pos + index) } } diff --git a/library/core/src/str/validations.rs b/library/core/src/str/validations.rs index b2ea86d699..0d3dc856be 100644 --- a/library/core/src/str/validations.rs +++ b/library/core/src/str/validations.rs @@ -273,16 +273,3 @@ pub const fn utf8_char_width(b: u8) -> usize { /// Mask of the value bits of a continuation byte. const CONT_MASK: u8 = 0b0011_1111; - -// truncate `&str` to length at most equal to `max` -// return `true` if it were truncated, and the new str. -pub(super) fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) { - if max >= s.len() { - (false, s) - } else { - while !s.is_char_boundary(max) { - max -= 1; - } - (true, &s[..max]) - } -} diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 1dd3b2d8e3..9ee88dd601 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -131,6 +131,7 @@ use crate::hint::spin_loop; /// loads and stores of `u8`. #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "AtomicBool"] #[repr(C, align(1))] pub struct AtomicBool { v: UnsafeCell, @@ -333,10 +334,10 @@ impl AtomicBool { #[inline] #[cfg(target_has_atomic_equal_alignment = "8")] #[unstable(feature = "atomic_from_mut", issue = "76314")] - pub fn from_mut(v: &mut bool) -> &Self { + pub fn from_mut(v: &mut bool) -> &mut Self { // SAFETY: the mutable reference guarantees unique ownership, and // alignment of both `bool` and `Self` is 1. - unsafe { &*(v as *mut bool as *mut Self) } + unsafe { &mut *(v as *mut bool as *mut Self) } } /// Consumes the atomic and returns the contained value. @@ -934,14 +935,14 @@ impl AtomicPtr { #[inline] #[cfg(target_has_atomic_equal_alignment = "ptr")] #[unstable(feature = "atomic_from_mut", issue = "76314")] - pub fn from_mut(v: &mut *mut T) -> &Self { + pub fn from_mut(v: &mut *mut T) -> &mut Self { use crate::mem::align_of; let [] = [(); align_of::>() - align_of::<*mut ()>()]; // SAFETY: // - the mutable reference guarantees unique ownership. // - the alignment of `*mut T` and `Self` is the same on all platforms // supported by rust, as verified above. - unsafe { &*(v as *mut *mut T as *mut Self) } + unsafe { &mut *(v as *mut *mut T as *mut Self) } } /// Consumes the atomic and returns the contained value. @@ -1294,6 +1295,7 @@ impl const From for AtomicBool { #[stable(feature = "atomic_from", since = "1.23.0")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] impl const From<*mut T> for AtomicPtr { + /// Converts a `*mut T` into an `AtomicPtr`. #[inline] fn from(p: *mut T) -> Self { Self::new(p) @@ -1447,14 +1449,14 @@ macro_rules! atomic_int { #[inline] #[$cfg_align] #[unstable(feature = "atomic_from_mut", issue = "76314")] - pub fn from_mut(v: &mut $int_type) -> &Self { + pub fn from_mut(v: &mut $int_type) -> &mut 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) } + unsafe { &mut *(v as *mut $int_type as *mut Self) } } /// Consumes the atomic and returns the contained value. diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 72a030617a..41f0a25dbc 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -243,7 +243,7 @@ impl Poll>> { #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_unstable(feature = "const_convert", issue = "88674")] impl const From for Poll { - /// Convert to a `Ready` variant. + /// Moves the value into a [`Poll::Ready`] to make a `Poll`. /// /// # Example /// diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 6cba781c2e..27af227a1f 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -43,6 +43,22 @@ impl RawWaker { pub const fn new(data: *const (), vtable: &'static RawWakerVTable) -> RawWaker { RawWaker { data, vtable } } + + /// Get the `data` pointer used to create this `RawWaker`. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "87021")] + pub fn data(&self) -> *const () { + self.data + } + + /// Get the `vtable` pointer used to create this `RawWaker`. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "87021")] + pub fn vtable(&self) -> &'static RawWakerVTable { + self.vtable + } } /// A virtual function pointer table (vtable) that specifies the behavior @@ -260,6 +276,14 @@ impl Waker { pub unsafe fn from_raw(waker: RawWaker) -> Waker { Waker { waker } } + + /// Get a reference to the underlying [`RawWaker`]. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "87021")] + pub fn as_raw(&self) -> &RawWaker { + &self.waker + } } #[stable(feature = "futures_api", since = "1.36.0")] diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 746d1cacfd..243c044b5d 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -711,14 +711,28 @@ impl Duration { /// as `f64`. /// /// # Panics - /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`. + /// This constructor will panic if `secs` is negative, overflows `Duration` or not finite. /// /// # Examples /// ``` /// use std::time::Duration; /// - /// let dur = Duration::from_secs_f64(2.7); - /// assert_eq!(dur, Duration::new(2, 700_000_000)); + /// let res = Duration::from_secs_f64(0.0); + /// assert_eq!(res, Duration::new(0, 0)); + /// let res = Duration::from_secs_f64(1e-20); + /// assert_eq!(res, Duration::new(0, 0)); + /// let res = Duration::from_secs_f64(4.2e-7); + /// assert_eq!(res, Duration::new(0, 420)); + /// let res = Duration::from_secs_f64(2.7); + /// assert_eq!(res, Duration::new(2, 700_000_000)); + /// let res = Duration::from_secs_f64(3e10); + /// assert_eq!(res, Duration::new(30_000_000_000, 0)); + /// // subnormal float + /// let res = Duration::from_secs_f64(f64::from_bits(1)); + /// assert_eq!(res, Duration::new(0, 0)); + /// // conversion uses truncation, not rounding + /// let res = Duration::from_secs_f64(0.999e-9); + /// assert_eq!(res, Duration::new(0, 0)); /// ``` #[stable(feature = "duration_float", since = "1.38.0")] #[must_use] @@ -731,55 +745,32 @@ impl Duration { } } - /// The checked version of [`from_secs_f64`]. - /// - /// [`from_secs_f64`]: Duration::from_secs_f64 - /// - /// This constructor will return an `Err` if `secs` is not finite, negative or overflows `Duration`. - /// - /// # Examples - /// ``` - /// #![feature(duration_checked_float)] - /// use std::time::Duration; - /// - /// let dur = Duration::try_from_secs_f64(2.7); - /// assert_eq!(dur, Ok(Duration::new(2, 700_000_000))); - /// - /// let negative = Duration::try_from_secs_f64(-5.0); - /// assert!(negative.is_err()); - /// ``` - #[unstable(feature = "duration_checked_float", issue = "83400")] - #[inline] - pub const fn try_from_secs_f64(secs: f64) -> Result { - const MAX_NANOS_F64: f64 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f64; - let nanos = secs * (NANOS_PER_SEC as f64); - if !nanos.is_finite() { - Err(FromSecsError { kind: FromSecsErrorKind::NonFinite }) - } else if nanos >= MAX_NANOS_F64 { - Err(FromSecsError { kind: FromSecsErrorKind::Overflow }) - } else if nanos < 0.0 { - Err(FromSecsError { kind: FromSecsErrorKind::Negative }) - } else { - let nanos = nanos as u128; - Ok(Duration { - secs: (nanos / (NANOS_PER_SEC as u128)) as u64, - nanos: (nanos % (NANOS_PER_SEC as u128)) as u32, - }) - } - } - /// Creates a new `Duration` from the specified number of seconds represented /// as `f32`. /// /// # Panics - /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`. + /// This constructor will panic if `secs` is negative, overflows `Duration` or not finite. /// /// # Examples /// ``` /// use std::time::Duration; /// - /// let dur = Duration::from_secs_f32(2.7); - /// assert_eq!(dur, Duration::new(2, 700_000_000)); + /// let res = Duration::from_secs_f32(0.0); + /// assert_eq!(res, Duration::new(0, 0)); + /// let res = Duration::from_secs_f32(1e-20); + /// assert_eq!(res, Duration::new(0, 0)); + /// let res = Duration::from_secs_f32(4.2e-7); + /// assert_eq!(res, Duration::new(0, 419)); + /// let res = Duration::from_secs_f32(2.7); + /// assert_eq!(res, Duration::new(2, 700_000_047)); + /// let res = Duration::from_secs_f32(3e10); + /// assert_eq!(res, Duration::new(30_000_001_024, 0)); + /// // subnormal float + /// let res = Duration::from_secs_f32(f32::from_bits(1)); + /// assert_eq!(res, Duration::new(0, 0)); + /// // conversion uses truncation, not rounding + /// let res = Duration::from_secs_f32(0.999e-9); + /// assert_eq!(res, Duration::new(0, 0)); /// ``` #[stable(feature = "duration_float", since = "1.38.0")] #[must_use] @@ -792,47 +783,10 @@ impl Duration { } } - /// The checked version of [`from_secs_f32`]. - /// - /// [`from_secs_f32`]: Duration::from_secs_f32 - /// - /// This constructor will return an `Err` if `secs` is not finite, negative or overflows `Duration`. - /// - /// # Examples - /// ``` - /// #![feature(duration_checked_float)] - /// use std::time::Duration; - /// - /// let dur = Duration::try_from_secs_f32(2.7); - /// assert_eq!(dur, Ok(Duration::new(2, 700_000_000))); - /// - /// let negative = Duration::try_from_secs_f32(-5.0); - /// assert!(negative.is_err()); - /// ``` - #[unstable(feature = "duration_checked_float", issue = "83400")] - #[inline] - pub const fn try_from_secs_f32(secs: f32) -> Result { - const MAX_NANOS_F32: f32 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f32; - let nanos = secs * (NANOS_PER_SEC as f32); - if !nanos.is_finite() { - Err(FromSecsError { kind: FromSecsErrorKind::NonFinite }) - } else if nanos >= MAX_NANOS_F32 { - Err(FromSecsError { kind: FromSecsErrorKind::Overflow }) - } else if nanos < 0.0 { - Err(FromSecsError { kind: FromSecsErrorKind::Negative }) - } else { - let nanos = nanos as u128; - Ok(Duration { - secs: (nanos / (NANOS_PER_SEC as u128)) as u64, - nanos: (nanos % (NANOS_PER_SEC as u128)) as u32, - }) - } - } - /// Multiplies `Duration` by `f64`. /// /// # Panics - /// This method will panic if result is not finite, negative or overflows `Duration`. + /// This method will panic if result is negative, overflows `Duration` or not finite. /// /// # Examples /// ``` @@ -854,17 +808,15 @@ impl Duration { /// Multiplies `Duration` by `f32`. /// /// # Panics - /// This method will panic if result is not finite, negative or overflows `Duration`. + /// This method will panic if result is negative, overflows `Duration` or not finite. /// /// # Examples /// ``` /// use std::time::Duration; /// /// let dur = Duration::new(2, 700_000_000); - /// // note that due to rounding errors result is slightly different - /// // from 8.478 and 847800.0 /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_640)); - /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847799, 969_120_256)); + /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847800, 0)); /// ``` #[stable(feature = "duration_float", since = "1.38.0")] #[must_use = "this returns the result of the operation, \ @@ -878,7 +830,7 @@ impl Duration { /// Divide `Duration` by `f64`. /// /// # Panics - /// This method will panic if result is not finite, negative or overflows `Duration`. + /// This method will panic if result is negative, overflows `Duration` or not finite. /// /// # Examples /// ``` @@ -901,7 +853,7 @@ impl Duration { /// Divide `Duration` by `f32`. /// /// # Panics - /// This method will panic if result is not finite, negative or overflows `Duration`. + /// This method will panic if result is negative, overflows `Duration` or not finite. /// /// # Examples /// ``` @@ -910,7 +862,7 @@ impl Duration { /// let dur = Duration::new(2, 700_000_000); /// // note that due to rounding errors result is slightly /// // different from 0.859_872_611 - /// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_576)); + /// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_579)); /// // note that truncation is used, not rounding /// assert_eq!(dur.div_f32(3.14e5), Duration::new(0, 8_598)); /// ``` @@ -1267,33 +1219,180 @@ impl fmt::Debug for Duration { /// ``` #[derive(Debug, Clone, PartialEq, Eq)] #[unstable(feature = "duration_checked_float", issue = "83400")] -pub struct FromSecsError { - kind: FromSecsErrorKind, +pub struct FromFloatSecsError { + kind: FromFloatSecsErrorKind, } -impl FromSecsError { +impl FromFloatSecsError { const fn description(&self) -> &'static str { match self.kind { - FromSecsErrorKind::NonFinite => "non-finite value when converting float to duration", - FromSecsErrorKind::Overflow => "overflow when converting float to duration", - FromSecsErrorKind::Negative => "negative value when converting float to duration", + FromFloatSecsErrorKind::Negative => { + "can not convert float seconds to Duration: value is negative" + } + FromFloatSecsErrorKind::OverflowOrNan => { + "can not convert float seconds to Duration: value is either too big or NaN" + } } } } #[unstable(feature = "duration_checked_float", issue = "83400")] -impl fmt::Display for FromSecsError { +impl fmt::Display for FromFloatSecsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.description(), f) + self.description().fmt(f) } } #[derive(Debug, Clone, PartialEq, Eq)] -enum FromSecsErrorKind { - // Value is not a finite value (either + or - infinity or NaN). - NonFinite, - // Value is too large to store in a `Duration`. - Overflow, +enum FromFloatSecsErrorKind { // Value is negative. Negative, + // Value is either too big to be represented as `Duration` or `NaN`. + OverflowOrNan, +} + +macro_rules! try_from_secs { + ( + secs = $secs: expr, + mantissa_bits = $mant_bits: literal, + exponent_bits = $exp_bits: literal, + offset = $offset: literal, + bits_ty = $bits_ty:ty, + double_ty = $double_ty:ty, + ) => {{ + const MIN_EXP: i16 = 1 - (1i16 << $exp_bits) / 2; + const MANT_MASK: $bits_ty = (1 << $mant_bits) - 1; + const EXP_MASK: $bits_ty = (1 << $exp_bits) - 1; + + if $secs.is_sign_negative() { + return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::Negative }); + } + + let bits = $secs.to_bits(); + let mant = (bits & MANT_MASK) | (MANT_MASK + 1); + let exp = ((bits >> $mant_bits) & EXP_MASK) as i16 + MIN_EXP; + + let (secs, nanos) = if exp < -30 { + // the input represents less than 1ns. + (0u64, 0u32) + } else if exp < 0 { + // the input is less than 1 second + let t = <$double_ty>::from(mant) << ($offset + exp); + let nanos = (u128::from(NANOS_PER_SEC) * u128::from(t)) >> ($mant_bits + $offset); + (0, nanos as u32) + } else if exp < $mant_bits { + let secs = mant >> ($mant_bits - exp); + let t = <$double_ty>::from((mant << exp) & MANT_MASK); + let nanos = (<$double_ty>::from(NANOS_PER_SEC) * t) >> $mant_bits; + (u64::from(secs), nanos as u32) + } else if exp < 64 { + // the input has no fractional part + let secs = u64::from(mant) << (exp - $mant_bits); + (secs, 0) + } else { + return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::OverflowOrNan }); + }; + + Ok(Duration { secs, nanos }) + }}; +} + +impl Duration { + /// The checked version of [`from_secs_f32`]. + /// + /// [`from_secs_f32`]: Duration::from_secs_f32 + /// + /// This constructor will return an `Err` if `secs` is negative, overflows `Duration` or not finite. + /// + /// # Examples + /// ``` + /// #![feature(duration_checked_float)] + /// + /// use std::time::Duration; + /// + /// let res = Duration::try_from_secs_f32(0.0); + /// assert_eq!(res, Ok(Duration::new(0, 0))); + /// let res = Duration::try_from_secs_f32(1e-20); + /// assert_eq!(res, Ok(Duration::new(0, 0))); + /// let res = Duration::try_from_secs_f32(4.2e-7); + /// assert_eq!(res, Ok(Duration::new(0, 419))); + /// let res = Duration::try_from_secs_f32(2.7); + /// assert_eq!(res, Ok(Duration::new(2, 700_000_047))); + /// let res = Duration::try_from_secs_f32(3e10); + /// assert_eq!(res, Ok(Duration::new(30_000_001_024, 0))); + /// // subnormal float: + /// let res = Duration::try_from_secs_f32(f32::from_bits(1)); + /// assert_eq!(res, Ok(Duration::new(0, 0))); + /// // conversion uses truncation, not rounding + /// let res = Duration::try_from_secs_f32(0.999e-9); + /// assert_eq!(res, Ok(Duration::new(0, 0))); + /// + /// let res = Duration::try_from_secs_f32(-5.0); + /// assert!(res.is_err()); + /// let res = Duration::try_from_secs_f32(f32::NAN); + /// assert!(res.is_err()); + /// let res = Duration::try_from_secs_f32(2e19); + /// assert!(res.is_err()); + /// ``` + #[unstable(feature = "duration_checked_float", issue = "83400")] + #[inline] + pub const fn try_from_secs_f32(secs: f32) -> Result { + try_from_secs!( + secs = secs, + mantissa_bits = 23, + exponent_bits = 8, + offset = 41, + bits_ty = u32, + double_ty = u64, + ) + } + + /// The checked version of [`from_secs_f64`]. + /// + /// [`from_secs_f64`]: Duration::from_secs_f64 + /// + /// This constructor will return an `Err` if `secs` is negative, overflows `Duration` or not finite. + /// + /// # Examples + /// ``` + /// #![feature(duration_checked_float)] + /// + /// use std::time::Duration; + /// + /// let res = Duration::try_from_secs_f64(0.0); + /// assert_eq!(res, Ok(Duration::new(0, 0))); + /// let res = Duration::try_from_secs_f64(1e-20); + /// assert_eq!(res, Ok(Duration::new(0, 0))); + /// let res = Duration::try_from_secs_f64(4.2e-7); + /// assert_eq!(res, Ok(Duration::new(0, 420))); + /// let res = Duration::try_from_secs_f64(2.7); + /// assert_eq!(res, Ok(Duration::new(2, 700_000_000))); + /// let res = Duration::try_from_secs_f64(3e10); + /// assert_eq!(res, Ok(Duration::new(30_000_000_000, 0))); + /// // subnormal float + /// let res = Duration::try_from_secs_f64(f64::from_bits(1)); + /// assert_eq!(res, Ok(Duration::new(0, 0))); + /// // conversion uses truncation, not rounding + /// let res = Duration::try_from_secs_f32(0.999e-9); + /// assert_eq!(res, Ok(Duration::new(0, 0))); + /// + /// let res = Duration::try_from_secs_f64(-5.0); + /// assert!(res.is_err()); + /// let res = Duration::try_from_secs_f64(f64::NAN); + /// assert!(res.is_err()); + /// let res = Duration::try_from_secs_f64(2e19); + /// assert!(res.is_err()); + /// ``` + #[unstable(feature = "duration_checked_float", issue = "83400")] + #[inline] + pub const fn try_from_secs_f64(secs: f64) -> Result { + try_from_secs!( + secs = secs, + mantissa_bits = 52, + exponent_bits = 11, + offset = 44, + bits_ty = u64, + double_ty = u128, + ) + } } diff --git a/library/core/tests/char.rs b/library/core/tests/char.rs index 2b857a6591..4c899b6eb4 100644 --- a/library/core/tests/char.rs +++ b/library/core/tests/char.rs @@ -308,6 +308,33 @@ fn test_decode_utf16() { check(&[0xD800, 0], &[Err(0xD800), Ok('\0')]); } +#[test] +fn test_decode_utf16_size_hint() { + fn check(s: &[u16]) { + let mut iter = char::decode_utf16(s.iter().cloned()); + + loop { + let count = iter.clone().count(); + let (lower, upper) = iter.size_hint(); + + assert!( + lower <= count && count <= upper.unwrap(), + "lower = {lower}, count = {count}, upper = {upper:?}" + ); + + if let None = iter.next() { + break; + } + } + } + + check(&[0xD800, 0xD800, 0xDC00]); + check(&[0xD800, 0xD800, 0x0]); + check(&[0xD800, 0x41, 0x42]); + check(&[0xD800, 0]); + check(&[0xD834, 0x006d]); +} + #[test] fn ed_iterator_specializations() { // Check counting diff --git a/library/core/tests/cmp.rs b/library/core/tests/cmp.rs index 58fee19ca7..2b234de679 100644 --- a/library/core/tests/cmp.rs +++ b/library/core/tests/cmp.rs @@ -204,7 +204,6 @@ fn cmp_default() { assert_eq!(Fool(false), Fool(true)); } -#[cfg(not(bootstrap))] mod const_cmp { use super::*; diff --git a/library/core/tests/future.rs b/library/core/tests/future.rs index 0ed8c52c21..74b6f74e40 100644 --- a/library/core/tests/future.rs +++ b/library/core/tests/future.rs @@ -118,3 +118,11 @@ fn block_on(fut: impl Future) { } } } + +// just tests by whether or not this compiles +fn _pending_impl_all_auto_traits() { + use std::panic::{RefUnwindSafe, UnwindSafe}; + fn all_auto_traits() {} + + all_auto_traits::>(); +} diff --git a/library/core/tests/hash/mod.rs b/library/core/tests/hash/mod.rs index 72ccdd4848..a173e461c6 100644 --- a/library/core/tests/hash/mod.rs +++ b/library/core/tests/hash/mod.rs @@ -146,3 +146,11 @@ fn test_build_hasher_object_safe() { let _: &dyn BuildHasher = &RandomState::new(); } + +// just tests by whether or not this compiles +fn _build_hasher_default_impl_all_auto_traits() { + use std::panic::{RefUnwindSafe, UnwindSafe}; + fn all_auto_traits() {} + + all_auto_traits::>(); +} diff --git a/library/core/tests/intrinsics.rs b/library/core/tests/intrinsics.rs index 7a2e4e2906..e050e81037 100644 --- a/library/core/tests/intrinsics.rs +++ b/library/core/tests/intrinsics.rs @@ -37,7 +37,6 @@ fn test_assume_can_be_in_const_contexts() { } #[test] -#[cfg(not(bootstrap))] const fn test_write_bytes_in_const_contexts() { use core::intrinsics::write_bytes; @@ -80,3 +79,25 @@ fn test_hints_in_const_contexts() { assert!(42u32 == core::hint::black_box(42u32)); } } + +#[cfg(not(bootstrap))] +#[test] +fn test_const_allocate_at_runtime() { + use core::intrinsics::const_allocate; + unsafe { + assert!(const_allocate(4, 4).is_null()); + } +} + +#[cfg(not(bootstrap))] +#[test] +fn test_const_deallocate_at_runtime() { + use core::intrinsics::const_deallocate; + const X: &u32 = &42u32; + let x = &0u32; + unsafe { + const_deallocate(X as *const _ as *mut u8, 4, 4); // nop + const_deallocate(x as *const _ as *mut u8, 4, 4); // nop + const_deallocate(core::ptr::null_mut(), 1, 1); // nop + } +} diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/core/tests/iter/adapters/intersperse.rs index b336c03b5a..72ae59b6b2 100644 --- a/library/core/tests/iter/adapters/intersperse.rs +++ b/library/core/tests/iter/adapters/intersperse.rs @@ -74,7 +74,7 @@ fn test_intersperse_with() { struct NotClone { u: u32, } - let r = vec![NotClone { u: 0 }, NotClone { u: 1 }] + let r = [NotClone { u: 0 }, NotClone { u: 1 }] .into_iter() .intersperse_with(|| NotClone { u: 2 }) .collect::>(); @@ -120,7 +120,7 @@ fn test_intersperse_fold() { #[test] fn test_intersperse_collect_string() { - let contents = vec![1, 2, 3]; + let contents = [1, 2, 3]; let contents_string = contents .into_iter() diff --git a/library/core/tests/iter/adapters/peekable.rs b/library/core/tests/iter/adapters/peekable.rs index 390414d4aa..c1a1c29b60 100644 --- a/library/core/tests/iter/adapters/peekable.rs +++ b/library/core/tests/iter/adapters/peekable.rs @@ -144,7 +144,7 @@ fn test_iterator_peekable_rfold() { #[test] fn test_iterator_peekable_next_if_eq() { // first, try on references - let xs = vec!["Heart", "of", "Gold"]; + let xs = ["Heart", "of", "Gold"]; let mut it = xs.into_iter().peekable(); // try before `peek()` assert_eq!(it.next_if_eq(&"trillian"), None); @@ -157,7 +157,7 @@ fn test_iterator_peekable_next_if_eq() { assert_eq!(it.next(), Some("Gold")); // make sure comparison works for owned values - let xs = vec![String::from("Ludicrous"), "speed".into()]; + let xs = [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())); @@ -167,7 +167,7 @@ fn test_iterator_peekable_next_if_eq() { #[test] fn test_iterator_peekable_mut() { - let mut it = vec![1, 2, 3].into_iter().peekable(); + let mut it = [1, 2, 3].into_iter().peekable(); if let Some(p) = it.peek_mut() { if *p == 1 { *p = 5; diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs index d38bca1e3b..cf69f0a7a4 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/core/tests/iter/traits/iterator.rs @@ -456,25 +456,25 @@ fn test_find_map() { #[test] fn test_try_reduce() { - let v: Vec = vec![1, 2, 3, 4, 5]; + let v = [1usize, 2, 3, 4, 5]; let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y)); assert_eq!(sum, Some(Some(15))); - let v: Vec = vec![1, 2, 3, 4, 5, usize::MAX]; + let v = [1, 2, 3, 4, 5, usize::MAX]; let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y)); assert_eq!(sum, None); - let v: Vec = Vec::new(); + let v: [usize; 0] = []; let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y)); assert_eq!(sum, Some(None)); - let v = vec!["1", "2", "3", "4", "5"]; + let v = ["1", "2", "3", "4", "5"]; let max = v.into_iter().try_reduce(|x, y| { if x.parse::().ok()? > y.parse::().ok()? { Some(x) } else { Some(y) } }); assert_eq!(max, Some(Some("5"))); - let v = vec!["1", "2", "3", "4", "5"]; + let v = ["1", "2", "3", "4", "5"]; let max: Result, ::Err> = v.into_iter().try_reduce(|x, y| { if x.parse::()? > y.parse::()? { Ok(x) } else { Ok(y) } @@ -496,3 +496,57 @@ fn test_collect() { let b: Vec = a.iter().cloned().collect(); assert!(a == b); } + +#[test] +fn test_try_collect() { + use core::ops::ControlFlow::{Break, Continue}; + + let u = vec![Some(1), Some(2), Some(3)]; + let v = u.into_iter().try_collect::>(); + assert_eq!(v, Some(vec![1, 2, 3])); + + let u = vec![Some(1), Some(2), None, Some(3)]; + let mut it = u.into_iter(); + let v = it.try_collect::>(); + assert_eq!(v, None); + let v = it.try_collect::>(); + assert_eq!(v, Some(vec![3])); + + let u: Vec> = vec![Ok(1), Ok(2), Ok(3)]; + let v = u.into_iter().try_collect::>(); + assert_eq!(v, Ok(vec![1, 2, 3])); + + let u = vec![Ok(1), Ok(2), Err(()), Ok(3)]; + let v = u.into_iter().try_collect::>(); + assert_eq!(v, Err(())); + + let numbers = vec![1, 2, 3, 4, 5]; + let all_positive = numbers + .iter() + .cloned() + .map(|n| if n > 0 { Some(n) } else { None }) + .try_collect::>(); + assert_eq!(all_positive, Some(numbers)); + + let numbers = vec![-2, -1, 0, 1, 2]; + let all_positive = + numbers.into_iter().map(|n| if n > 0 { Some(n) } else { None }).try_collect::>(); + assert_eq!(all_positive, None); + + let u = [Continue(1), Continue(2), Break(3), Continue(4), Continue(5)]; + let mut it = u.into_iter(); + + let v = it.try_collect::>(); + assert_eq!(v, Break(3)); + + let v = it.try_collect::>(); + assert_eq!(v, Continue(vec![4, 5])); +} + +// just tests by whether or not this compiles +fn _empty_impl_all_auto_traits() { + use std::panic::{RefUnwindSafe, UnwindSafe}; + fn all_auto_traits() {} + + all_auto_traits::>(); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index ec700346ac..06c7be054a 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -6,16 +6,20 @@ #![feature(bool_to_option)] #![feature(box_syntax)] #![feature(cell_update)] -#![feature(cfg_panic)] -#![feature(cfg_target_has_atomic)] +#![cfg_attr(bootstrap, feature(cfg_panic))] +#![cfg_attr(bootstrap, feature(cfg_target_has_atomic))] #![feature(const_assume)] #![feature(const_black_box)] #![feature(const_bool_to_option)] #![feature(const_cell_into_inner)] #![feature(const_convert)] +#![feature(const_heap)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_assume_init)] +#![feature(const_maybe_uninit_assume_init_read)] +#![feature(const_nonnull_new)] #![feature(const_num_from_num)] +#![feature(const_ptr_as_ref)] #![feature(const_ptr_read)] #![feature(const_ptr_write)] #![feature(const_ptr_offset)] @@ -41,12 +45,11 @@ #![feature(inline_const)] #![feature(is_sorted)] #![feature(pattern)] +#![feature(pin_macro)] #![feature(sort_internals)] -#![feature(slice_partition_at_index)] #![feature(slice_take)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_array_assume_init)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(numfmt)] @@ -64,6 +67,7 @@ #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] #![feature(iter_order_by)] +#![feature(iterator_try_collect)] #![feature(iterator_try_reduce)] #![feature(const_mut_refs)] #![feature(const_pin)] @@ -88,6 +92,7 @@ #![feature(unzip_option)] #![feature(const_array_from_ref)] #![feature(const_slice_from_ref)] +#![feature(waker_getters)] #![deny(unsafe_op_in_unsafe_fn)] extern crate test; @@ -119,6 +124,7 @@ mod ops; mod option; mod pattern; mod pin; +mod pin_macro; mod ptr; mod result; mod simd; @@ -129,3 +135,4 @@ mod task; mod time; mod tuple; mod unicode; +mod waker; diff --git a/library/core/tests/num/bignum.rs b/library/core/tests/num/bignum.rs index 1457064cc8..416e7cea7a 100644 --- a/library/core/tests/num/bignum.rs +++ b/library/core/tests/num/bignum.rs @@ -1,4 +1,5 @@ use core::num::bignum::tests::Big8x3 as Big; +use core::num::bignum::Big32x40; #[test] #[should_panic] @@ -215,6 +216,16 @@ fn test_get_bit_out_of_range() { #[test] fn test_bit_length() { + for i in 0..8 * 3 { + // 010000...000 + assert_eq!(Big::from_small(1).mul_pow2(i).bit_length(), i + 1); + } + for i in 1..8 * 3 - 1 { + // 010000...001 + assert_eq!(Big::from_small(1).mul_pow2(i).add(&Big::from_small(1)).bit_length(), i + 1); + // 110000...000 + assert_eq!(Big::from_small(3).mul_pow2(i).bit_length(), i + 2); + } assert_eq!(Big::from_small(0).bit_length(), 0); assert_eq!(Big::from_small(1).bit_length(), 1); assert_eq!(Big::from_small(5).bit_length(), 3); @@ -223,6 +234,30 @@ fn test_bit_length() { assert_eq!(Big::from_u64(0xffffff).bit_length(), 24); } +#[test] +fn test_bit_length_32x40() { + for i in 0..32 * 40 { + // 010000...000 + assert_eq!(Big32x40::from_small(1).mul_pow2(i).bit_length(), i + 1); + } + for i in 1..32 * 40 - 1 { + // 010000...001 + assert_eq!( + Big32x40::from_small(1).mul_pow2(i).add(&Big32x40::from_small(1)).bit_length(), + i + 1 + ); + // 110000...000 + assert_eq!(Big32x40::from_small(3).mul_pow2(i).bit_length(), i + 2); + } + assert_eq!(Big32x40::from_small(0).bit_length(), 0); + assert_eq!(Big32x40::from_small(1).bit_length(), 1); + assert_eq!(Big32x40::from_small(5).bit_length(), 3); + assert_eq!(Big32x40::from_small(0x18).bit_length(), 5); + assert_eq!(Big32x40::from_u64(0x4073).bit_length(), 15); + assert_eq!(Big32x40::from_u64(0xffffff).bit_length(), 24); + assert_eq!(Big32x40::from_u64(0xffffffffffffffff).bit_length(), 64); +} + #[test] fn test_ord() { assert!(Big::from_u64(0) < Big::from_u64(0xffffff)); diff --git a/library/core/tests/ops.rs b/library/core/tests/ops.rs index aa79dbac8f..0c81cba35b 100644 --- a/library/core/tests/ops.rs +++ b/library/core/tests/ops.rs @@ -232,3 +232,9 @@ fn deref_on_ref() { let y = deref(&mut x); assert_eq!(y, 4); } + +#[test] +#[allow(unreachable_code)] +fn test_not_never() { + if !return () {} +} diff --git a/library/core/tests/pin_macro.rs b/library/core/tests/pin_macro.rs new file mode 100644 index 0000000000..79c8c166c5 --- /dev/null +++ b/library/core/tests/pin_macro.rs @@ -0,0 +1,33 @@ +// edition:2021 +use core::{ + marker::PhantomPinned, + mem::{drop as stuff, transmute}, + pin::{pin, Pin}, +}; + +#[test] +fn basic() { + let it: Pin<&mut PhantomPinned> = pin!(PhantomPinned); + stuff(it); +} + +#[test] +fn extension_works_through_block() { + let it: Pin<&mut PhantomPinned> = { pin!(PhantomPinned) }; + stuff(it); +} + +#[test] +fn extension_works_through_unsafe_block() { + // "retro-type-inference" works as well. + let it: Pin<&mut PhantomPinned> = unsafe { pin!(transmute(())) }; + stuff(it); +} + +#[test] +fn unsize_coercion() { + let slice: Pin<&mut [PhantomPinned]> = pin!([PhantomPinned; 2]); + stuff(slice); + let dyn_obj: Pin<&mut dyn Send> = pin!([PhantomPinned; 2]); + stuff(dyn_obj); +} diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index b9c0d75b70..9c65871ac4 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -251,7 +251,6 @@ fn test_set_memory() { } #[test] -#[cfg(not(bootstrap))] fn test_set_memory_const() { const XS: [u8; 20] = { let mut xs = [0u8; 20]; @@ -274,6 +273,21 @@ fn test_unsized_nonnull() { assert!(ys == zs); } +#[test] +fn test_const_nonnull_new() { + const { + assert!(NonNull::new(core::ptr::null_mut::<()>()).is_none()); + + let value = &mut 0u32; + let mut ptr = NonNull::new(value).unwrap(); + unsafe { *ptr.as_mut() = 42 }; + + let reference = unsafe { &*ptr.as_ref() }; + assert!(*reference == *value); + assert!(*reference == 42); + }; +} + #[test] #[allow(warnings)] // Have a symbol for the test below. It doesn’t need to be an actual variadic function, match the diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 20e2d8d47c..88cbd7352a 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -251,6 +251,40 @@ fn test_chunks_nth() { assert_eq!(c2.next(), None); } +#[test] +fn test_chunks_next() { + let v = [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks(2); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next().unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + assert_eq!(c.next(), None); + + let v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.chunks(3); + assert_eq!(c.next().unwrap(), &[0, 1, 2]); + assert_eq!(c.next().unwrap(), &[3, 4, 5]); + assert_eq!(c.next().unwrap(), &[6, 7]); + assert_eq!(c.next(), None); +} + +#[test] +fn test_chunks_next_back() { + let v = [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks(2); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + assert_eq!(c.next_back().unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[0, 1]); + assert_eq!(c.next_back(), None); + + let v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.chunks(3); + assert_eq!(c.next_back().unwrap(), &[6, 7]); + assert_eq!(c.next_back().unwrap(), &[3, 4, 5]); + assert_eq!(c.next_back().unwrap(), &[0, 1, 2]); + assert_eq!(c.next_back(), None); +} + #[test] fn test_chunks_nth_back() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; @@ -809,6 +843,40 @@ fn test_rchunks_nth_back() { assert_eq!(c2.next_back(), None); } +#[test] +fn test_rchunks_next() { + let v = [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks(2); + assert_eq!(c.next().unwrap(), &[4, 5]); + assert_eq!(c.next().unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.rchunks(3); + assert_eq!(c.next().unwrap(), &[5, 6, 7]); + assert_eq!(c.next().unwrap(), &[2, 3, 4]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); +} + +#[test] +fn test_rchunks_next_back() { + let v = [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks(2); + assert_eq!(c.next_back().unwrap(), &[0, 1]); + assert_eq!(c.next_back().unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + assert_eq!(c.next_back(), None); + + let v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.rchunks(3); + assert_eq!(c.next_back().unwrap(), &[0, 1]); + assert_eq!(c.next_back().unwrap(), &[2, 3, 4]); + assert_eq!(c.next_back().unwrap(), &[5, 6, 7]); + assert_eq!(c.next_back(), None); +} + #[test] fn test_rchunks_last() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; @@ -874,6 +942,40 @@ fn test_rchunks_mut_nth_back() { assert_eq!(c2.next_back(), None); } +#[test] +fn test_rchunks_mut_next() { + let mut v = [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_mut(2); + assert_eq!(c.next().unwrap(), &mut [4, 5]); + assert_eq!(c.next().unwrap(), &mut [2, 3]); + assert_eq!(c.next().unwrap(), &mut [0, 1]); + assert_eq!(c.next(), None); + + let mut v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.rchunks_mut(3); + assert_eq!(c.next().unwrap(), &mut [5, 6, 7]); + assert_eq!(c.next().unwrap(), &mut [2, 3, 4]); + assert_eq!(c.next().unwrap(), &mut [0, 1]); + assert_eq!(c.next(), None); +} + +#[test] +fn test_rchunks_mut_next_back() { + let mut v = [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_mut(2); + assert_eq!(c.next_back().unwrap(), &mut [0, 1]); + assert_eq!(c.next_back().unwrap(), &mut [2, 3]); + assert_eq!(c.next_back().unwrap(), &mut [4, 5]); + assert_eq!(c.next_back(), None); + + let mut v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.rchunks_mut(3); + assert_eq!(c.next_back().unwrap(), &mut [0, 1]); + assert_eq!(c.next_back().unwrap(), &mut [2, 3, 4]); + assert_eq!(c.next_back().unwrap(), &mut [5, 6, 7]); + assert_eq!(c.next_back(), None); +} + #[test] fn test_rchunks_mut_last() { let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; diff --git a/library/core/tests/waker.rs b/library/core/tests/waker.rs new file mode 100644 index 0000000000..6602ab36ba --- /dev/null +++ b/library/core/tests/waker.rs @@ -0,0 +1,22 @@ +use std::ptr; +use std::task::{RawWaker, RawWakerVTable, Waker}; + +#[test] +fn test_waker_getters() { + let raw_waker = RawWaker::new(42usize as *mut (), &WAKER_VTABLE); + assert_eq!(raw_waker.data() as usize, 42); + assert!(ptr::eq(raw_waker.vtable(), &WAKER_VTABLE)); + + let waker = unsafe { Waker::from_raw(raw_waker) }; + let waker2 = waker.clone(); + let raw_waker2 = waker2.as_raw(); + assert_eq!(raw_waker2.data() as usize, 43); + assert!(ptr::eq(raw_waker2.vtable(), &WAKER_VTABLE)); +} + +static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( + |data| RawWaker::new((data as usize + 1) as *mut (), &WAKER_VTABLE), + |_| {}, + |_| {}, + |_| {}, +); diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index 6dec0e6749..46183d1ad0 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "Implementation of Rust panics via process aborts" -edition = "2018" +edition = "2021" [lib] test = false diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index 67405463aa..d720cc7bcb 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "Implementation of Rust panics via stack unwinding" -edition = "2018" +edition = "2021" [lib] test = false diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index 9d6ede73e3..a0297b4b2f 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -105,6 +105,9 @@ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1 +#[cfg(target_arch = "m68k")] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // D0, D1 + #[cfg(any(target_arch = "mips", target_arch = "mips64"))] const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1 diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index e5753ccae2..7f05c82ac2 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -39,6 +39,10 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "hermit")] { #[path = "hermit.rs"] mod real_imp; + } else if #[cfg(target_os = "l4re")] { + // L4Re is unix family but does not yet support unwinding. + #[path = "dummy.rs"] + mod real_imp; } else if #[cfg(target_env = "msvc")] { #[path = "seh.rs"] mod real_imp; diff --git a/library/portable-simd/Cargo.toml b/library/portable-simd/Cargo.toml index 3f1abd7351..9802386e45 100644 --- a/library/portable-simd/Cargo.toml +++ b/library/portable-simd/Cargo.toml @@ -2,5 +2,6 @@ members = [ "crates/core_simd", + "crates/std_float", "crates/test_helpers", ] diff --git a/library/portable-simd/crates/core_simd/Cargo.toml b/library/portable-simd/crates/core_simd/Cargo.toml index a103ef115a..d2ff5f3b1b 100644 --- a/library/portable-simd/crates/core_simd/Cargo.toml +++ b/library/portable-simd/crates/core_simd/Cargo.toml @@ -26,3 +26,6 @@ features = ["alloc"] [dev-dependencies.test_helpers] path = "../test_helpers" + +[dev-dependencies] +std_float = { path = "../std_float/", features = ["as_crate"] } diff --git a/library/portable-simd/crates/core_simd/examples/nbody.rs b/library/portable-simd/crates/core_simd/examples/nbody.rs index 43280feebb..7b1e6840f6 100644 --- a/library/portable-simd/crates/core_simd/examples/nbody.rs +++ b/library/portable-simd/crates/core_simd/examples/nbody.rs @@ -1,11 +1,13 @@ -#![cfg_attr(feature = "std", feature(portable_simd))] +#![feature(portable_simd)] +extern crate std_float; /// Benchmarks game nbody code /// Taken from the `packed_simd` crate /// Run this benchmark with `cargo test --example nbody` -#[cfg(feature = "std")] mod nbody { - use core_simd::*; + use core_simd::simd::*; + #[allow(unused)] // False positive? + use std_float::StdFloat; use std::f64::consts::PI; const SOLAR_MASS: f64 = 4.0 * PI * PI; @@ -167,7 +169,6 @@ mod nbody { } } -#[cfg(feature = "std")] #[cfg(test)] mod tests { // Good enough for demonstration purposes, not going for strictness here. @@ -184,7 +185,6 @@ mod tests { } fn main() { - #[cfg(feature = "std")] { let (energy_before, energy_after) = nbody::run(1000); println!("Energy before: {}", energy_before); diff --git a/library/portable-simd/crates/core_simd/src/intrinsics.rs b/library/portable-simd/crates/core_simd/src/intrinsics.rs index 6a6d26d10a..233657202f 100644 --- a/library/portable-simd/crates/core_simd/src/intrinsics.rs +++ b/library/portable-simd/crates/core_simd/src/intrinsics.rs @@ -39,6 +39,10 @@ extern "platform-intrinsic" { /// fptoui/fptosi/uitofp/sitofp pub(crate) fn simd_cast(x: T) -> U; + /// follows Rust's `T as U` semantics, including saturating float casts + /// which amounts to the same as `simd_cast` for many cases + #[cfg(not(bootstrap))] + pub(crate) fn simd_as(x: T) -> U; /// neg/fneg pub(crate) fn simd_neg(x: T) -> T; @@ -46,6 +50,10 @@ extern "platform-intrinsic" { /// fabs pub(crate) fn simd_fabs(x: T) -> T; + // minnum/maxnum + pub(crate) fn simd_fmin(x: T, y: T) -> T; + pub(crate) fn simd_fmax(x: T, y: T) -> T; + pub(crate) fn simd_eq(x: T, y: T) -> U; pub(crate) fn simd_ne(x: T, y: T) -> U; pub(crate) fn simd_lt(x: T, y: T) -> U; @@ -87,29 +95,3 @@ extern "platform-intrinsic" { #[allow(unused)] pub(crate) fn simd_select_bitmask(m: M, a: T, b: T) -> T; } - -#[cfg(feature = "std")] -mod std { - extern "platform-intrinsic" { - // ceil - pub(crate) fn simd_ceil(x: T) -> T; - - // floor - pub(crate) fn simd_floor(x: T) -> T; - - // round - pub(crate) fn simd_round(x: T) -> T; - - // trunc - pub(crate) fn simd_trunc(x: T) -> T; - - // fsqrt - pub(crate) fn simd_fsqrt(x: T) -> T; - - // fma - pub(crate) fn simd_fma(x: T, y: T, z: T) -> T; - } -} - -#[cfg(feature = "std")] -pub(crate) use crate::simd::intrinsics::std::*; diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index 191e969031..ae1fef53da 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -12,9 +12,10 @@ )] mod mask_impl; +use crate::simd::intrinsics; use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; use core::cmp::Ordering; -use core::fmt; +use core::{fmt, mem}; mod sealed { use super::*; @@ -105,22 +106,39 @@ where Self(mask_impl::Mask::splat(value)) } - /// Converts an array to a SIMD vector. + /// Converts an array of bools to a SIMD mask. pub fn from_array(array: [bool; LANES]) -> Self { - let mut vector = Self::splat(false); - for (i, v) in array.iter().enumerate() { - vector.set(i, *v); + // SAFETY: Rust's bool has a layout of 1 byte (u8) with a value of + // true: 0b_0000_0001 + // false: 0b_0000_0000 + // Thus, an array of bools is also a valid array of bytes: [u8; N] + // This would be hypothetically valid as an "in-place" transmute, + // but these are "dependently-sized" types, so copy elision it is! + unsafe { + let bytes: [u8; LANES] = mem::transmute_copy(&array); + let bools: Simd = + intrinsics::simd_ne(Simd::from_array(bytes), Simd::splat(0u8)); + Mask::from_int_unchecked(intrinsics::simd_cast(bools)) } - vector } - /// Converts a SIMD vector to an array. + /// Converts a SIMD mask to an array of bools. pub fn to_array(self) -> [bool; LANES] { - let mut array = [false; LANES]; - for (i, v) in array.iter_mut().enumerate() { - *v = self.test(i); + // This follows mostly the same logic as from_array. + // SAFETY: Rust's bool has a layout of 1 byte (u8) with a value of + // true: 0b_0000_0001 + // false: 0b_0000_0000 + // Thus, an array of bools is also a valid array of bytes: [u8; N] + // Since our masks are equal to integers where all bits are set, + // we can simply convert them to i8s, and then bitand them by the + // bitpattern for Rust's "true" bool. + // This would be hypothetically valid as an "in-place" transmute, + // but these are "dependently-sized" types, so copy elision it is! + unsafe { + let mut bytes: Simd = intrinsics::simd_cast(self.to_int()); + bytes &= Simd::splat(1i8); + mem::transmute_copy(&bytes) } - array } /// Converts a vector of integers to a mask, where 0 represents `false` and -1 @@ -516,7 +534,7 @@ pub type mask16x8 = Mask; pub type mask16x16 = Mask; /// Vector of 32 16-bit masks -pub type mask16x32 = Mask; +pub type mask16x32 = Mask; /// Vector of two 32-bit masks pub type mask32x2 = Mask; diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index 3582c57870..b65038933b 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -1,4 +1,3 @@ -use crate::simd::intrinsics; use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; @@ -32,232 +31,211 @@ where } } -/// Checks if the right-hand side argument of a left- or right-shift would cause overflow. -fn invalid_shift_rhs(rhs: T) -> bool -where - T: Default + PartialOrd + core::convert::TryFrom, - >::Error: core::fmt::Debug, -{ - let bits_in_type = T::try_from(8 * core::mem::size_of::()).unwrap(); - rhs < T::default() || rhs >= bits_in_type +macro_rules! unsafe_base { + ($lhs:ident, $rhs:ident, {$simd_call:ident}, $($_:tt)*) => { + unsafe { $crate::simd::intrinsics::$simd_call($lhs, $rhs) } + }; } -/// Automatically implements operators over references in addition to the provided operator. -macro_rules! impl_ref_ops { - // binary op - { - impl core::ops::$trait:ident<$rhs:ty> for $type:ty - where - LaneCount<$lanes2:ident>: SupportedLaneCount, - { - type Output = $output:ty; - - $(#[$attrs:meta])* - fn $fn:ident($self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) -> Self::Output $body:tt - } - } => { - impl core::ops::$trait<$rhs> for $type - where - LaneCount<$lanes2>: SupportedLaneCount, - { - type Output = $output; - - $(#[$attrs])* - fn $fn($self_tok, $rhs_arg: $rhs_arg_ty) -> Self::Output $body +/// SAFETY: This macro should not be used for anything except Shl or Shr, and passed the appropriate shift intrinsic. +/// It handles performing a bitand in addition to calling the shift operator, so that the result +/// is well-defined: LLVM can return a poison value if you shl, lshr, or ashr if rhs >= ::BITS +/// At worst, this will maybe add another instruction and cycle, +/// at best, it may open up more optimization opportunities, +/// or simply be elided entirely, especially for SIMD ISAs which default to this. +/// +// FIXME: Consider implementing this in cg_llvm instead? +// cg_clif defaults to this, and scalar MIR shifts also default to wrapping +macro_rules! wrap_bitshift { + ($lhs:ident, $rhs:ident, {$simd_call:ident}, $int:ident) => { + unsafe { + $crate::simd::intrinsics::$simd_call( + $lhs, + $rhs.bitand(Simd::splat(<$int>::BITS as $int - 1)), + ) } }; } -/// Automatically implements operators over vectors and scalars for a particular vector. -macro_rules! impl_op { - { impl Add for $scalar:ty } => { - impl_op! { @binary $scalar, Add::add, simd_add } - }; - { impl Sub for $scalar:ty } => { - impl_op! { @binary $scalar, Sub::sub, simd_sub } - }; - { impl Mul for $scalar:ty } => { - impl_op! { @binary $scalar, Mul::mul, simd_mul } - }; - { impl Div for $scalar:ty } => { - impl_op! { @binary $scalar, Div::div, simd_div } - }; - { impl Rem for $scalar:ty } => { - impl_op! { @binary $scalar, Rem::rem, simd_rem } - }; - { impl Shl for $scalar:ty } => { - impl_op! { @binary $scalar, Shl::shl, simd_shl } - }; - { impl Shr for $scalar:ty } => { - impl_op! { @binary $scalar, Shr::shr, simd_shr } - }; - { impl BitAnd for $scalar:ty } => { - impl_op! { @binary $scalar, BitAnd::bitand, simd_and } - }; - { impl BitOr for $scalar:ty } => { - impl_op! { @binary $scalar, BitOr::bitor, simd_or } - }; - { impl BitXor for $scalar:ty } => { - impl_op! { @binary $scalar, BitXor::bitxor, simd_xor } +// Division by zero is poison, according to LLVM. +// So is dividing the MIN value of a signed integer by -1, +// since that would return MAX + 1. +// FIXME: Rust allows ::MIN / -1, +// so we should probably figure out how to make that safe. +macro_rules! int_divrem_guard { + ( $lhs:ident, + $rhs:ident, + { const PANIC_ZERO: &'static str = $zero:literal; + const PANIC_OVERFLOW: &'static str = $overflow:literal; + $simd_call:ident + }, + $int:ident ) => { + if $rhs.lanes_eq(Simd::splat(0)).any() { + panic!($zero); + } else if <$int>::MIN != 0 + && ($lhs.lanes_eq(Simd::splat(<$int>::MIN)) + // type inference can break here, so cut an SInt to size + & $rhs.lanes_eq(Simd::splat(-1i64 as _))).any() + { + panic!($overflow); + } else { + unsafe { $crate::simd::intrinsics::$simd_call($lhs, $rhs) } + } }; +} - // generic binary op with assignment when output is `Self` - { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $intrinsic:ident } => { - impl_ref_ops! { - impl core::ops::$trait for Simd<$scalar, LANES> - where - LaneCount: SupportedLaneCount, - { - type Output = Self; +macro_rules! for_base_types { + ( T = ($($scalar:ident),*); + type Lhs = Simd; + type Rhs = Simd; + type Output = $out:ty; + + impl $op:ident::$call:ident { + $macro_impl:ident $inner:tt + }) => { + $( + impl $op for Simd<$scalar, N> + where + $scalar: SimdElement, + LaneCount: SupportedLaneCount, + { + type Output = $out; - #[inline] - fn $trait_fn(self, rhs: Self) -> Self::Output { - unsafe { - intrinsics::$intrinsic(self, rhs) + #[inline] + #[must_use = "operator returns a new vector without mutating the inputs"] + fn $call(self, rhs: Self) -> Self::Output { + $macro_impl!(self, rhs, $inner, $scalar) } - } - } - } - }; + })* + } } -/// Implements floating-point operators for the provided types. -macro_rules! impl_float_ops { - { $($scalar:ty),* } => { - $( - impl_op! { impl Add for $scalar } - impl_op! { impl Sub for $scalar } - impl_op! { impl Mul for $scalar } - impl_op! { impl Div for $scalar } - impl_op! { impl Rem for $scalar } - )* +// A "TokenTree muncher": takes a set of scalar types `T = {};` +// type parameters for the ops it implements, `Op::fn` names, +// and a macro that expands into an expr, substituting in an intrinsic. +// It passes that to for_base_types, which expands an impl for the types, +// using the expanded expr in the function, and recurses with itself. +// +// tl;dr impls a set of ops::{Traits} for a set of types +macro_rules! for_base_ops { + ( + T = $types:tt; + type Lhs = Simd; + type Rhs = Simd; + type Output = $out:ident; + impl $op:ident::$call:ident + $inner:tt + $($rest:tt)* + ) => { + for_base_types! { + T = $types; + type Lhs = Simd; + type Rhs = Simd; + type Output = $out; + impl $op::$call + $inner + } + for_base_ops! { + T = $types; + type Lhs = Simd; + type Rhs = Simd; + type Output = $out; + $($rest)* + } }; + ($($done:tt)*) => { + // Done. + } } -/// Implements unsigned integer operators for the provided types. -macro_rules! impl_unsigned_int_ops { - { $($scalar:ty),* } => { - $( - impl_op! { impl Add for $scalar } - impl_op! { impl Sub for $scalar } - impl_op! { impl Mul for $scalar } - impl_op! { impl BitAnd for $scalar } - impl_op! { impl BitOr for $scalar } - impl_op! { impl BitXor for $scalar } +// Integers can always accept add, mul, sub, bitand, bitor, and bitxor. +// For all of these operations, simd_* intrinsics apply wrapping logic. +for_base_ops! { + T = (i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); + type Lhs = Simd; + type Rhs = Simd; + type Output = Self; - // Integers panic on divide by 0 - impl_ref_ops! { - impl core::ops::Div for Simd<$scalar, LANES> - where - LaneCount: SupportedLaneCount, - { - type Output = Self; + impl Add::add { + unsafe_base { simd_add } + } - #[inline] - fn div(self, rhs: Self) -> Self::Output { - if rhs.as_array() - .iter() - .any(|x| *x == 0) - { - panic!("attempt to divide by zero"); - } + impl Mul::mul { + unsafe_base { simd_mul } + } - // Guards for div(MIN, -1), - // this check only applies to signed ints - if <$scalar>::MIN != 0 && self.as_array().iter() - .zip(rhs.as_array().iter()) - .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) { - panic!("attempt to divide with overflow"); - } - unsafe { intrinsics::simd_div(self, rhs) } - } - } - } + impl Sub::sub { + unsafe_base { simd_sub } + } - // remainder panics on zero divisor - impl_ref_ops! { - impl core::ops::Rem for Simd<$scalar, LANES> - where - LaneCount: SupportedLaneCount, - { - type Output = Self; + impl BitAnd::bitand { + unsafe_base { simd_and } + } - #[inline] - fn rem(self, rhs: Self) -> Self::Output { - if rhs.as_array() - .iter() - .any(|x| *x == 0) - { - panic!("attempt to calculate the remainder with a divisor of zero"); - } + impl BitOr::bitor { + unsafe_base { simd_or } + } - // Guards for rem(MIN, -1) - // this branch applies the check only to signed ints - if <$scalar>::MIN != 0 && self.as_array().iter() - .zip(rhs.as_array().iter()) - .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) { - panic!("attempt to calculate the remainder with overflow"); - } - unsafe { intrinsics::simd_rem(self, rhs) } - } - } - } + impl BitXor::bitxor { + unsafe_base { simd_xor } + } - // shifts panic on overflow - impl_ref_ops! { - impl core::ops::Shl for Simd<$scalar, LANES> - where - LaneCount: SupportedLaneCount, - { - type Output = Self; + impl Div::div { + int_divrem_guard { + const PANIC_ZERO: &'static str = "attempt to divide by zero"; + const PANIC_OVERFLOW: &'static str = "attempt to divide with overflow"; + simd_div + } + } - #[inline] - fn shl(self, rhs: Self) -> Self::Output { - // TODO there is probably a better way of doing this - if rhs.as_array() - .iter() - .copied() - .any(invalid_shift_rhs) - { - panic!("attempt to shift left with overflow"); - } - unsafe { intrinsics::simd_shl(self, rhs) } - } - } - } + impl Rem::rem { + int_divrem_guard { + const PANIC_ZERO: &'static str = "attempt to calculate the remainder with a divisor of zero"; + const PANIC_OVERFLOW: &'static str = "attempt to calculate the remainder with overflow"; + simd_rem + } + } - impl_ref_ops! { - impl core::ops::Shr for Simd<$scalar, LANES> - where - LaneCount: SupportedLaneCount, - { - type Output = Self; + // The only question is how to handle shifts >= ::BITS? + // Our current solution uses wrapping logic. + impl Shl::shl { + wrap_bitshift { simd_shl } + } - #[inline] - fn shr(self, rhs: Self) -> Self::Output { - // TODO there is probably a better way of doing this - if rhs.as_array() - .iter() - .copied() - .any(invalid_shift_rhs) - { - panic!("attempt to shift with overflow"); - } - unsafe { intrinsics::simd_shr(self, rhs) } - } - } - } - )* - }; + impl Shr::shr { + wrap_bitshift { + // This automatically monomorphizes to lshr or ashr, depending, + // so it's fine to use it for both UInts and SInts. + simd_shr + } + } } -/// Implements unsigned integer operators for the provided types. -macro_rules! impl_signed_int_ops { - { $($scalar:ty),* } => { - impl_unsigned_int_ops! { $($scalar),* } - }; -} +// We don't need any special precautions here: +// Floats always accept arithmetic ops, but may become NaN. +for_base_ops! { + T = (f32, f64); + type Lhs = Simd; + type Rhs = Simd; + type Output = Self; + + impl Add::add { + unsafe_base { simd_add } + } -impl_unsigned_int_ops! { u8, u16, u32, u64, usize } -impl_signed_int_ops! { i8, i16, i32, i64, isize } -impl_float_ops! { f32, f64 } + impl Mul::mul { + unsafe_base { simd_mul } + } + + impl Sub::sub { + unsafe_base { simd_sub } + } + + impl Div::div { + unsafe_base { simd_div } + } + + impl Rem::rem { + unsafe_base { simd_rem } + } +} diff --git a/library/portable-simd/crates/core_simd/src/round.rs b/library/portable-simd/crates/core_simd/src/round.rs index 09789e1149..06ccab3ec4 100644 --- a/library/portable-simd/crates/core_simd/src/round.rs +++ b/library/portable-simd/crates/core_simd/src/round.rs @@ -5,47 +5,6 @@ macro_rules! implement { { $type:ty, $int_type:ty } => { - #[cfg(feature = "std")] - impl Simd<$type, LANES> - where - LaneCount: SupportedLaneCount, - { - /// Returns the smallest integer greater than or equal to each lane. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - pub fn ceil(self) -> Self { - unsafe { intrinsics::simd_ceil(self) } - } - - /// Returns the largest integer value less than or equal to each lane. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - pub fn floor(self) -> Self { - unsafe { intrinsics::simd_floor(self) } - } - - /// Rounds to the nearest integer value. Ties round toward zero. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - pub fn round(self) -> Self { - unsafe { intrinsics::simd_round(self) } - } - - /// Returns the floating point's integer value, with its fractional part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - pub fn trunc(self) -> Self { - unsafe { intrinsics::simd_trunc(self) } - } - - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - pub fn fract(self) -> Self { - self - self.trunc() - } - } - impl Simd<$type, LANES> where LaneCount: SupportedLaneCount, diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 7c5ec2bc31..b7ef7a56c7 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -75,6 +75,36 @@ where Self(array) } + /// Performs lanewise conversion of a SIMD vector's elements to another SIMD-valid type. + /// This follows the semantics of Rust's `as` conversion for casting + /// integers to unsigned integers (interpreting as the other type, so `-1` to `MAX`), + /// and from floats to integers (truncating, or saturating at the limits) for each lane, + /// or vice versa. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "std")] use core_simd::Simd; + /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// let floats: Simd = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]); + /// let ints = floats.cast::(); + /// assert_eq!(ints, Simd::from_array([1, -4, i32::MAX, 0])); + /// + /// // Formally equivalent, but `Simd::cast` can optimize better. + /// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32))); + /// + /// // The float conversion does not round-trip. + /// let floats_again = ints.cast(); + /// assert_ne!(floats, floats_again); + /// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0])); + /// ``` + #[must_use] + #[inline] + #[cfg(not(bootstrap))] + pub fn cast(self) -> Simd { + unsafe { intrinsics::simd_as(self) } + } + /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the lane is instead selected from the `or` vector. /// diff --git a/library/portable-simd/crates/core_simd/src/vector/float.rs b/library/portable-simd/crates/core_simd/src/vector/float.rs index 4a4b23238c..fcc7f6d8d1 100644 --- a/library/portable-simd/crates/core_simd/src/vector/float.rs +++ b/library/portable-simd/crates/core_simd/src/vector/float.rs @@ -38,29 +38,6 @@ macro_rules! impl_float_vector { unsafe { intrinsics::simd_fabs(self) } } - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, - /// yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target - /// architecture has a dedicated `fma` CPU instruction. However, this is not always - /// true, and will be heavily dependent on designing algorithms with specific target - /// hardware in mind. - #[cfg(feature = "std")] - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn mul_add(self, a: Self, b: Self) -> Self { - unsafe { intrinsics::simd_fma(self, a, b) } - } - - /// Produces a vector where every lane has the square root value - /// of the equivalently-indexed lane in `self` - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - #[cfg(feature = "std")] - pub fn sqrt(self) -> Self { - unsafe { intrinsics::simd_fsqrt(self) } - } - /// Takes the reciprocal (inverse) of each lane, `1/x`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] @@ -128,8 +105,8 @@ macro_rules! impl_float_vector { self.abs().lanes_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(<$type>::INFINITY).to_bits()).lanes_eq(Simd::splat(0)) } - /// Returns true for each lane if its value is neither neither zero, infinite, - /// subnormal, or `NaN`. + /// Returns true for each lane if its value is neither zero, infinite, + /// subnormal, nor `NaN`. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn is_normal(self) -> Mask<$mask_ty, LANES> { @@ -164,11 +141,7 @@ macro_rules! impl_float_vector { #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] pub fn min(self, other: Self) -> Self { - // TODO consider using an intrinsic - self.is_nan().select( - other, - self.lanes_ge(other).select(other, self) - ) + unsafe { intrinsics::simd_fmin(self, other) } } /// Returns the maximum of each lane. @@ -177,11 +150,7 @@ macro_rules! impl_float_vector { #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] pub fn max(self, other: Self) -> Self { - // TODO consider using an intrinsic - self.is_nan().select( - other, - self.lanes_le(other).select(other, self) - ) + unsafe { intrinsics::simd_fmax(self, other) } } /// Restrict each lane to a certain interval unless it is NaN. diff --git a/library/portable-simd/crates/core_simd/tests/cast.rs b/library/portable-simd/crates/core_simd/tests/cast.rs new file mode 100644 index 0000000000..ab5650f071 --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/cast.rs @@ -0,0 +1,37 @@ +#![feature(portable_simd)] +macro_rules! cast_types { + ($start:ident, $($target:ident),*) => { + mod $start { + use core_simd::simd::Simd; + type Vector = Simd<$start, N>; + $( + mod $target { + use super::*; + test_helpers::test_lanes! { + fn cast_as() { + test_helpers::test_unary_elementwise( + &Vector::::cast::<$target>, + &|x| x as $target, + &|_| true, + ) + } + } + } + )* + } + }; +} + +// The hypothesis is that widening conversions aren't terribly interesting. +cast_types!(f32, f64, i8, u8, usize, isize); +cast_types!(f64, f32, i8, u8, usize, isize); +cast_types!(i8, u8, f32); +cast_types!(u8, i8, f32); +cast_types!(i16, u16, i8, u8, f32); +cast_types!(u16, i16, i8, u8, f32); +cast_types!(i32, u32, i8, u8, f32, f64); +cast_types!(u32, i32, i8, u8, f32, f64); +cast_types!(i64, u64, i8, u8, isize, usize, f32, f64); +cast_types!(u64, i64, i8, u8, isize, usize, f32, f64); +cast_types!(isize, usize, i8, u8, f32, f64); +cast_types!(usize, isize, i8, u8, f32, f64); diff --git a/library/portable-simd/crates/core_simd/tests/ops_macros.rs b/library/portable-simd/crates/core_simd/tests/ops_macros.rs index 43ddde4c55..4fb9de198e 100644 --- a/library/portable-simd/crates/core_simd/tests/ops_macros.rs +++ b/library/portable-simd/crates/core_simd/tests/ops_macros.rs @@ -546,6 +546,8 @@ macro_rules! impl_float_tests { #[cfg(feature = "std")] mod std { + use std_float::StdFloat; + use super::*; test_helpers::test_lanes! { fn sqrt() { diff --git a/library/portable-simd/crates/core_simd/tests/round.rs b/library/portable-simd/crates/core_simd/tests/round.rs index 11d617a6c2..1a1bc9ebca 100644 --- a/library/portable-simd/crates/core_simd/tests/round.rs +++ b/library/portable-simd/crates/core_simd/tests/round.rs @@ -3,6 +3,8 @@ macro_rules! float_rounding_test { { $scalar:tt, $int_scalar:tt } => { mod $scalar { + use std_float::StdFloat; + type Vector = core_simd::Simd<$scalar, LANES>; type Scalar = $scalar; type IntScalar = $int_scalar; diff --git a/library/portable-simd/crates/std_float/Cargo.toml b/library/portable-simd/crates/std_float/Cargo.toml new file mode 100644 index 0000000000..82f66b8dcb --- /dev/null +++ b/library/portable-simd/crates/std_float/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "std_float" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +core_simd = { path = "../core_simd" } + +[features] +default = ["as_crate"] +as_crate = [] diff --git a/library/portable-simd/crates/std_float/src/lib.rs b/library/portable-simd/crates/std_float/src/lib.rs new file mode 100644 index 0000000000..4bd4d4c05e --- /dev/null +++ b/library/portable-simd/crates/std_float/src/lib.rs @@ -0,0 +1,165 @@ +#![cfg_attr(feature = "as_crate", no_std)] // We are std! +#![cfg_attr( + feature = "as_crate", + feature(platform_intrinsics), + feature(portable_simd) +)] +#[cfg(not(feature = "as_crate"))] +use core::simd; +#[cfg(feature = "as_crate")] +use core_simd::simd; + +use simd::{LaneCount, Simd, SupportedLaneCount}; + +#[cfg(feature = "as_crate")] +mod experimental { + pub trait Sealed {} +} + +#[cfg(feature = "as_crate")] +use experimental as sealed; + +use crate::sealed::Sealed; + +// "platform intrinsics" are essentially "codegen intrinsics" +// each of these may be scalarized and lowered to a libm call +extern "platform-intrinsic" { + // ceil + fn simd_ceil(x: T) -> T; + + // floor + fn simd_floor(x: T) -> T; + + // round + fn simd_round(x: T) -> T; + + // trunc + fn simd_trunc(x: T) -> T; + + // fsqrt + fn simd_fsqrt(x: T) -> T; + + // fma + fn simd_fma(x: T, y: T, z: T) -> T; +} + +/// This trait provides a possibly-temporary implementation of float functions +/// that may, in the absence of hardware support, canonicalize to calling an +/// operating system's `math.h` dynamically-loaded library (also known as a +/// shared object). As these conditionally require runtime support, they +/// should only appear in binaries built assuming OS support: `std`. +/// +/// However, there is no reason SIMD types, in general, need OS support, +/// as for many architectures an embedded binary may simply configure that +/// support itself. This means these types must be visible in `core` +/// but have these functions available in `std`. +/// +/// [`f32`] and [`f64`] achieve a similar trick by using "lang items", but +/// due to compiler limitations, it is harder to implement this approach for +/// abstract data types like [`Simd`]. From that need, this trait is born. +/// +/// It is possible this trait will be replaced in some manner in the future, +/// when either the compiler or its supporting runtime functions are improved. +/// For now this trait is available to permit experimentation with SIMD float +/// operations that may lack hardware support, such as `mul_add`. +pub trait StdFloat: Sealed + Sized { + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, + /// yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target + /// architecture has a dedicated `fma` CPU instruction. However, this is not always + /// true, and will be heavily dependent on designing algorithms with specific target + /// hardware in mind. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn mul_add(self, a: Self, b: Self) -> Self { + unsafe { simd_fma(self, a, b) } + } + + /// Produces a vector where every lane has the square root value + /// of the equivalently-indexed lane in `self` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn sqrt(self) -> Self { + unsafe { simd_fsqrt(self) } + } + + /// Returns the smallest integer greater than or equal to each lane. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn ceil(self) -> Self { + unsafe { simd_ceil(self) } + } + + /// Returns the largest integer value less than or equal to each lane. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn floor(self) -> Self { + unsafe { simd_floor(self) } + } + + /// Rounds to the nearest integer value. Ties round toward zero. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn round(self) -> Self { + unsafe { simd_round(self) } + } + + /// Returns the floating point's integer value, with its fractional part removed. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn trunc(self) -> Self { + unsafe { simd_trunc(self) } + } + + /// Returns the floating point's fractional value, with its integer part removed. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn fract(self) -> Self; +} + +impl Sealed for Simd where LaneCount: SupportedLaneCount {} +impl Sealed for Simd where LaneCount: SupportedLaneCount {} + +// We can safely just use all the defaults. +impl StdFloat for Simd +where + LaneCount: SupportedLaneCount, +{ + /// Returns the floating point's fractional value, with its integer part removed. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } +} + +impl StdFloat for Simd +where + LaneCount: SupportedLaneCount, +{ + /// Returns the floating point's fractional value, with its integer part removed. + #[must_use = "method returns a new vector and does not mutate the original value"] + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use simd::*; + + #[test] + fn everything_works() { + let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); + let x2 = x + x; + let _xc = x.ceil(); + let _xf = x.floor(); + let _xr = x.round(); + let _xt = x.trunc(); + let _xfma = x.mul_add(x, x); + let _xsqrt = x.sqrt(); + let _ = x2.abs() * x2; + } +} diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index faf460e32b..db5e2e4e24 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "proc_macro" version = "0.0.0" -edition = "2018" +edition = "2021" [dependencies] std = { path = "../std" } diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 83a2ac6f0d..9e9750eb8d 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -310,8 +310,7 @@ impl Bridge<'_> { // NB. the server can't do this because it may use a different libstd. static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); HIDE_PANICS_DURING_EXPANSION.call_once(|| { - let prev = panic::take_hook(); - panic::set_hook(Box::new(move |info| { + panic::update_hook(move |prev, info| { let show = BridgeState::with(|state| match state { BridgeState::NotConnected => true, BridgeState::Connected(_) | BridgeState::InUse => force_show_panics, @@ -319,7 +318,7 @@ impl Bridge<'_> { if show { prev(info) } - })); + }); }); BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f)) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 69af598f91..c5afca6d56 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -30,6 +30,7 @@ #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(min_specialization)] +#![feature(panic_update_hook)] #![recursion_limit = "256"] #[unstable(feature = "proc_macro_internals", issue = "27812")] diff --git a/library/profiler_builtins/Cargo.toml b/library/profiler_builtins/Cargo.toml index 0f7f006765..3371dfa124 100644 --- a/library/profiler_builtins/Cargo.toml +++ b/library/profiler_builtins/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "profiler_builtins" version = "0.0.0" -edition = "2018" +edition = "2021" [lib] test = false diff --git a/library/rustc-std-workspace-alloc/Cargo.toml b/library/rustc-std-workspace-alloc/Cargo.toml index 1ea421834a..049ca3e46b 100644 --- a/library/rustc-std-workspace-alloc/Cargo.toml +++ b/library/rustc-std-workspace-alloc/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2018" +edition = "2021" [lib] path = "lib.rs" diff --git a/library/rustc-std-workspace-core/Cargo.toml b/library/rustc-std-workspace-core/Cargo.toml index 01e8b92e14..ff5cfcbd64 100644 --- a/library/rustc-std-workspace-core/Cargo.toml +++ b/library/rustc-std-workspace-core/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2018" +edition = "2021" [lib] path = "lib.rs" diff --git a/library/rustc-std-workspace-std/Cargo.toml b/library/rustc-std-workspace-std/Cargo.toml index 811bc78d21..3a1dc2a02b 100644 --- a/library/rustc-std-workspace-std/Cargo.toml +++ b/library/rustc-std-workspace-std/Cargo.toml @@ -5,7 +5,7 @@ license = 'MIT OR Apache-2.0' description = """ Hack for the compiler's own build system """ -edition = "2018" +edition = "2021" [lib] path = "lib.rs" diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index bf54004964..146096535d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -15,11 +15,11 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.108", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.67" } +libc = { version = "0.2.116", default-features = false, features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.69" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep-of-std'] } +hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] } std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate @@ -45,7 +45,7 @@ fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } hermit-abi = { version = "0.1.19", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] -wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false } +wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } [features] backtrace = [ diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index efd5785f4c..53b43455b5 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -371,6 +371,7 @@ impl HashMap { /// assert_eq!(vec, ["a", "b", "c"]); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "map_into_keys_values", since = "1.54.0")] pub fn into_keys(self) -> IntoKeys { IntoKeys { inner: self.into_iter() } @@ -448,6 +449,7 @@ impl HashMap { /// assert_eq!(vec, [1, 2, 3]); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "map_into_keys_values", since = "1.54.0")] pub fn into_values(self) -> IntoValues { IntoValues { inner: self.into_iter() } @@ -471,6 +473,7 @@ impl HashMap { /// println!("key: {} val: {}", key, val); /// } /// ``` + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "rust1", since = "1.0.0")] pub fn iter(&self) -> Iter<'_, K, V> { Iter { base: self.base.iter() } @@ -500,6 +503,7 @@ impl HashMap { /// println!("key: {} val: {}", key, val); /// } /// ``` + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "rust1", since = "1.0.0")] pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { IterMut { base: self.base.iter_mut() } @@ -543,6 +547,10 @@ impl HashMap { /// Clears the map, returning all key-value pairs as an iterator. Keeps the /// allocated memory for reuse. /// + /// If the returned iterator is dropped before being fully consumed, it + /// drops the remaining key-value pairs. The returned iterator keeps a + /// mutable borrow on the vector to optimize its implementation. + /// /// # Examples /// /// ``` @@ -560,6 +568,7 @@ impl HashMap { /// assert!(a.is_empty()); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "drain", since = "1.6.0")] pub fn drain(&mut self) -> Drain<'_, K, V> { Drain { base: self.base.drain() } @@ -601,6 +610,7 @@ impl HashMap { /// assert_eq!(odds, vec![1, 3, 5, 7]); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[unstable(feature = "hash_drain_filter", issue = "59618")] pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> where @@ -624,6 +634,7 @@ impl HashMap { /// assert_eq!(map.len(), 4); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "retain_hash_collection", since = "1.18.0")] pub fn retain(&mut self, f: F) where @@ -1990,6 +2001,7 @@ impl<'a, K, V, S> IntoIterator for &'a HashMap { type IntoIter = Iter<'a, K, V>; #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] fn into_iter(self) -> Iter<'a, K, V> { self.iter() } @@ -2001,6 +2013,7 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap { type IntoIter = IterMut<'a, K, V>; #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] fn into_iter(self) -> IterMut<'a, K, V> { self.iter_mut() } @@ -2030,6 +2043,7 @@ impl IntoIterator for HashMap { /// let vec: Vec<(&str, i32)> = map.into_iter().collect(); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] fn into_iter(self) -> IntoIter { IntoIter { base: self.base.into_iter() } } diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index d9b20aee2d..30da22b808 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -420,8 +420,8 @@ fn test_iterate() { #[test] fn test_keys() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = pairs.into_iter().collect(); let keys: Vec<_> = map.keys().cloned().collect(); assert_eq!(keys.len(), 3); assert!(keys.contains(&1)); @@ -431,8 +431,8 @@ fn test_keys() { #[test] fn test_values() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = pairs.into_iter().collect(); let values: Vec<_> = map.values().cloned().collect(); assert_eq!(values.len(), 3); assert!(values.contains(&'a')); @@ -442,8 +442,8 @@ fn test_values() { #[test] fn test_values_mut() { - let vec = vec![(1, 1), (2, 2), (3, 3)]; - let mut map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 1), (2, 2), (3, 3)]; + let mut map: HashMap<_, _> = pairs.into_iter().collect(); for value in map.values_mut() { *value = (*value) * 2 } @@ -456,8 +456,8 @@ fn test_values_mut() { #[test] fn test_into_keys() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = pairs.into_iter().collect(); let keys: Vec<_> = map.into_keys().collect(); assert_eq!(keys.len(), 3); @@ -468,8 +468,8 @@ fn test_into_keys() { #[test] fn test_into_values() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = vec.into_iter().collect(); + let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; + let map: HashMap<_, _> = pairs.into_iter().collect(); let values: Vec<_> = map.into_values().collect(); assert_eq!(values.len(), 3); @@ -817,6 +817,7 @@ fn test_retain() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { let mut empty_bytes: HashMap = HashMap::new(); @@ -828,11 +829,21 @@ fn test_try_reserve() { "usize::MAX should trigger an overflow!" ); - assert_matches!( - empty_bytes.try_reserve(MAX_USIZE / 8).map_err(|e| e.kind()), - Err(AllocError { .. }), - "usize::MAX / 8 should trigger an OOM!" - ); + if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 16).map_err(|e| e.kind()) { + } else { + // This may succeed if there is enough free memory. Attempt to + // allocate a few more hashmaps to ensure the allocation will fail. + let mut empty_bytes2: HashMap = HashMap::new(); + let _ = empty_bytes2.try_reserve(MAX_USIZE / 16); + let mut empty_bytes3: HashMap = HashMap::new(); + let _ = empty_bytes3.try_reserve(MAX_USIZE / 16); + let mut empty_bytes4: HashMap = HashMap::new(); + assert_matches!( + empty_bytes4.try_reserve(MAX_USIZE / 16).map_err(|e| e.kind()), + Err(AllocError { .. }), + "usize::MAX / 16 should trigger an OOM!" + ); + } } #[test] diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 0d087772bf..200667ae39 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -185,6 +185,7 @@ impl HashSet { /// } /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "rust1", since = "1.0.0")] pub fn iter(&self) -> Iter<'_, T> { Iter { base: self.base.iter() } @@ -226,7 +227,12 @@ impl HashSet { self.base.is_empty() } - /// Clears the set, returning all elements in an iterator. + /// Clears the set, returning all elements as an iterator. Keeps the + /// allocated memory for reuse. + /// + /// If the returned iterator is dropped before being fully consumed, it + /// drops the remaining elements. The returned iterator keeps a mutable + /// borrow on the vector to optimize its implementation. /// /// # Examples /// @@ -244,6 +250,7 @@ impl HashSet { /// assert!(set.is_empty()); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "drain", since = "1.6.0")] pub fn drain(&mut self) -> Drain<'_, T> { Drain { base: self.base.drain() } @@ -282,6 +289,7 @@ impl HashSet { /// assert_eq!(odds, vec![1, 3, 5, 7]); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[unstable(feature = "hash_drain_filter", issue = "59618")] pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, T, F> where @@ -304,6 +312,7 @@ impl HashSet { /// set.retain(|&k| k % 2 == 0); /// assert_eq!(set.len(), 3); /// ``` + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "retain_hash_collection", since = "1.18.0")] pub fn retain(&mut self, f: F) where @@ -528,6 +537,7 @@ where /// assert_eq!(diff, [4].iter().collect()); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "rust1", since = "1.0.0")] pub fn difference<'a>(&'a self, other: &'a HashSet) -> Difference<'a, T, S> { Difference { iter: self.iter(), other } @@ -555,6 +565,7 @@ where /// assert_eq!(diff1, [1, 4].iter().collect()); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "rust1", since = "1.0.0")] pub fn symmetric_difference<'a>( &'a self, @@ -582,6 +593,7 @@ where /// assert_eq!(intersection, [2, 3].iter().collect()); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "rust1", since = "1.0.0")] pub fn intersection<'a>(&'a self, other: &'a HashSet) -> Intersection<'a, T, S> { if self.len() <= other.len() { @@ -610,6 +622,7 @@ where /// assert_eq!(union, [1, 2, 3, 4].iter().collect()); /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] #[stable(feature = "rust1", since = "1.0.0")] pub fn union<'a>(&'a self, other: &'a HashSet) -> Union<'a, T, S> { if self.len() >= other.len() { @@ -1410,6 +1423,7 @@ impl<'a, T, S> IntoIterator for &'a HashSet { type IntoIter = Iter<'a, T>; #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] fn into_iter(self) -> Iter<'a, T> { self.iter() } @@ -1441,6 +1455,7 @@ impl IntoIterator for HashSet { /// } /// ``` #[inline] + #[cfg_attr(not(bootstrap), rustc_lint_query_instability)] fn into_iter(self) -> IntoIter { IntoIter { base: self.base.into_iter() } } diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 8b004525b4..b5e81deb48 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -232,7 +232,7 @@ //! ``` //! use std::collections::VecDeque; //! -//! let vec = vec![1, 2, 3, 4]; +//! let vec = [1, 2, 3, 4]; //! let buf: VecDeque<_> = vec.into_iter().collect(); //! ``` //! diff --git a/library/std/src/env.rs b/library/std/src/env.rs index c06928647d..5ed9fa9d6f 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -23,6 +23,11 @@ use crate::sys::os as os_imp; /// Returns the current working directory as a [`PathBuf`]. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `getcwd` function on Unix +/// and the `GetCurrentDirectoryW` function on Windows. +/// /// # Errors /// /// Returns an [`Err`] if the current working directory value is invalid. @@ -49,6 +54,11 @@ pub fn current_dir() -> io::Result { /// Changes the current working directory to the specified path. /// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `chdir` function on Unix +/// and the `SetCurrentDirectoryW` function on Windows. +/// /// Returns an [`Err`] if the operation fails. /// /// # Examples diff --git a/library/std/src/error.rs b/library/std/src/error.rs index ea0c230fa4..1a96b9c928 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -25,7 +25,7 @@ use crate::backtrace::Backtrace; use crate::borrow::Cow; use crate::cell; use crate::char; -use crate::fmt::{self, Debug, Display}; +use crate::fmt::{self, Debug, Display, Write}; use crate::mem::transmute; use crate::num; use crate::str; @@ -63,7 +63,7 @@ pub trait Error: Debug + Display { /// /// #[derive(Debug)] /// struct SuperError { - /// side: SuperErrorSideKick, + /// source: SuperErrorSideKick, /// } /// /// impl fmt::Display for SuperError { @@ -74,7 +74,7 @@ pub trait Error: Debug + Display { /// /// impl Error for SuperError { /// fn source(&self) -> Option<&(dyn Error + 'static)> { - /// Some(&self.side) + /// Some(&self.source) /// } /// } /// @@ -90,7 +90,7 @@ pub trait Error: Debug + Display { /// impl Error for SuperErrorSideKick {} /// /// fn get_super_error() -> Result<(), SuperError> { - /// Err(SuperError { side: SuperErrorSideKick }) + /// Err(SuperError { source: SuperErrorSideKick }) /// } /// /// fn main() { @@ -602,25 +602,25 @@ impl Error for char::ParseCharError { impl Error for alloc::collections::TryReserveError {} #[unstable(feature = "duration_checked_float", issue = "83400")] -impl Error for time::FromSecsError {} +impl Error for time::FromFloatSecsError {} // Copied from `any.rs`. impl dyn Error + 'static { - /// Returns `true` if the boxed type is the same as `T` + /// Returns `true` if the inner type is the same as `T`. #[stable(feature = "error_downcast", since = "1.3.0")] #[inline] pub fn is(&self) -> bool { // Get `TypeId` of the type this function is instantiated with. let t = TypeId::of::(); - // Get `TypeId` of the type in the trait object. - let boxed = self.type_id(private::Internal); + // Get `TypeId` of the type in the trait object (`self`). + let concrete = self.type_id(private::Internal); // Compare both `TypeId`s on equality. - t == boxed + t == concrete } - /// Returns some reference to the boxed value if it is of type `T`, or + /// Returns some reference to the inner value if it is of type `T`, or /// `None` if it isn't. #[stable(feature = "error_downcast", since = "1.3.0")] #[inline] @@ -632,7 +632,7 @@ impl dyn Error + 'static { } } - /// Returns some mutable reference to the boxed value if it is of type `T`, or + /// Returns some mutable reference to the inner value if it is of type `T`, or /// `None` if it isn't. #[stable(feature = "error_downcast", since = "1.3.0")] #[inline] @@ -810,3 +810,642 @@ impl dyn Error + Send + Sync { }) } } + +/// An error reporter that print's an error and its sources. +/// +/// Report also exposes configuration options for formatting the error chain, either entirely on a +/// single line, or in multi-line format with each cause in the error chain on a new line. +/// +/// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the +/// wrapped error be `Send`, `Sync`, or `'static`. +/// +/// # Examples +/// +/// ```rust +/// #![feature(error_reporter)] +/// use std::error::{Error, Report}; +/// use std::fmt; +/// +/// #[derive(Debug)] +/// struct SuperError { +/// source: SuperErrorSideKick, +/// } +/// +/// impl fmt::Display for SuperError { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "SuperError is here!") +/// } +/// } +/// +/// impl Error for SuperError { +/// fn source(&self) -> Option<&(dyn Error + 'static)> { +/// Some(&self.source) +/// } +/// } +/// +/// #[derive(Debug)] +/// struct SuperErrorSideKick; +/// +/// impl fmt::Display for SuperErrorSideKick { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "SuperErrorSideKick is here!") +/// } +/// } +/// +/// impl Error for SuperErrorSideKick {} +/// +/// fn get_super_error() -> Result<(), SuperError> { +/// Err(SuperError { source: SuperErrorSideKick }) +/// } +/// +/// fn main() { +/// match get_super_error() { +/// Err(e) => println!("Error: {}", Report::new(e)), +/// _ => println!("No error"), +/// } +/// } +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here!: SuperErrorSideKick is here! +/// ``` +/// +/// ## Output consistency +/// +/// Report prints the same output via `Display` and `Debug`, so it works well with +/// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`: +/// +/// ```should_panic +/// #![feature(error_reporter)] +/// use std::error::Report; +/// # use std::error::Error; +/// # use std::fmt; +/// # #[derive(Debug)] +/// # struct SuperError { +/// # source: SuperErrorSideKick, +/// # } +/// # impl fmt::Display for SuperError { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperError is here!") +/// # } +/// # } +/// # impl Error for SuperError { +/// # fn source(&self) -> Option<&(dyn Error + 'static)> { +/// # Some(&self.source) +/// # } +/// # } +/// # #[derive(Debug)] +/// # struct SuperErrorSideKick; +/// # impl fmt::Display for SuperErrorSideKick { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperErrorSideKick is here!") +/// # } +/// # } +/// # impl Error for SuperErrorSideKick {} +/// # fn get_super_error() -> Result<(), SuperError> { +/// # Err(SuperError { source: SuperErrorSideKick }) +/// # } +/// +/// get_super_error().map_err(Report::new).unwrap(); +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40 +/// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +/// ``` +/// +/// ## Return from `main` +/// +/// `Report` also implements `From` for all types that implement [`Error`], this when combined with +/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned +/// from `main`. +/// +/// ```should_panic +/// #![feature(error_reporter)] +/// use std::error::Report; +/// # use std::error::Error; +/// # use std::fmt; +/// # #[derive(Debug)] +/// # struct SuperError { +/// # source: SuperErrorSideKick, +/// # } +/// # impl fmt::Display for SuperError { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperError is here!") +/// # } +/// # } +/// # impl Error for SuperError { +/// # fn source(&self) -> Option<&(dyn Error + 'static)> { +/// # Some(&self.source) +/// # } +/// # } +/// # #[derive(Debug)] +/// # struct SuperErrorSideKick; +/// # impl fmt::Display for SuperErrorSideKick { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperErrorSideKick is here!") +/// # } +/// # } +/// # impl Error for SuperErrorSideKick {} +/// # fn get_super_error() -> Result<(), SuperError> { +/// # Err(SuperError { source: SuperErrorSideKick }) +/// # } +/// +/// fn main() -> Result<(), Report> { +/// get_super_error()?; +/// Ok(()) +/// } +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here!: SuperErrorSideKick is here! +/// ``` +/// +/// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line +/// output format, if you want to make sure your `Report`s are pretty printed and include backtrace +/// you will need to manually convert and enable those flags. +/// +/// ```should_panic +/// #![feature(error_reporter)] +/// use std::error::Report; +/// # use std::error::Error; +/// # use std::fmt; +/// # #[derive(Debug)] +/// # struct SuperError { +/// # source: SuperErrorSideKick, +/// # } +/// # impl fmt::Display for SuperError { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperError is here!") +/// # } +/// # } +/// # impl Error for SuperError { +/// # fn source(&self) -> Option<&(dyn Error + 'static)> { +/// # Some(&self.source) +/// # } +/// # } +/// # #[derive(Debug)] +/// # struct SuperErrorSideKick; +/// # impl fmt::Display for SuperErrorSideKick { +/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// # write!(f, "SuperErrorSideKick is here!") +/// # } +/// # } +/// # impl Error for SuperErrorSideKick {} +/// # fn get_super_error() -> Result<(), SuperError> { +/// # Err(SuperError { source: SuperErrorSideKick }) +/// # } +/// +/// fn main() -> Result<(), Report> { +/// get_super_error() +/// .map_err(Report::from) +/// .map_err(|r| r.pretty(true).show_backtrace(true))?; +/// Ok(()) +/// } +/// ``` +/// +/// This example produces the following output: +/// +/// ```console +/// Error: SuperError is here! +/// +/// Caused by: +/// SuperErrorSideKick is here! +/// ``` +#[unstable(feature = "error_reporter", issue = "90172")] +pub struct Report> { + /// The error being reported. + error: E, + /// Whether a backtrace should be included as part of the report. + show_backtrace: bool, + /// Whether the report should be pretty-printed. + pretty: bool, +} + +impl Report +where + Report: From, +{ + /// Create a new `Report` from an input error. + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn new(error: E) -> Report { + Self::from(error) + } +} + +impl Report { + /// Enable pretty-printing the report across multiple lines. + /// + /// # Examples + /// + /// ```rust + /// #![feature(error_reporter)] + /// use std::error::Report; + /// # use std::error::Error; + /// # use std::fmt; + /// # #[derive(Debug)] + /// # struct SuperError { + /// # source: SuperErrorSideKick, + /// # } + /// # impl fmt::Display for SuperError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperError is here!") + /// # } + /// # } + /// # impl Error for SuperError { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// # #[derive(Debug)] + /// # struct SuperErrorSideKick; + /// # impl fmt::Display for SuperErrorSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKick is here!") + /// # } + /// # } + /// # impl Error for SuperErrorSideKick {} + /// + /// let error = SuperError { source: SuperErrorSideKick }; + /// let report = Report::new(error).pretty(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// SuperErrorSideKick is here! + /// ``` + /// + /// When there are multiple source errors the causes will be numbered in order of iteration + /// starting from the outermost error. + /// + /// ```rust + /// #![feature(error_reporter)] + /// use std::error::Report; + /// # use std::error::Error; + /// # use std::fmt; + /// # #[derive(Debug)] + /// # struct SuperError { + /// # source: SuperErrorSideKick, + /// # } + /// # impl fmt::Display for SuperError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperError is here!") + /// # } + /// # } + /// # impl Error for SuperError { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// # #[derive(Debug)] + /// # struct SuperErrorSideKick { + /// # source: SuperErrorSideKickSideKick, + /// # } + /// # impl fmt::Display for SuperErrorSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKick is here!") + /// # } + /// # } + /// # impl Error for SuperErrorSideKick { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// # #[derive(Debug)] + /// # struct SuperErrorSideKickSideKick; + /// # impl fmt::Display for SuperErrorSideKickSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKickSideKick is here!") + /// # } + /// # } + /// # impl Error for SuperErrorSideKickSideKick { } + /// + /// let source = SuperErrorSideKickSideKick; + /// let source = SuperErrorSideKick { source }; + /// let error = SuperError { source }; + /// let report = Report::new(error).pretty(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// 0: SuperErrorSideKick is here! + /// 1: SuperErrorSideKickSideKick is here! + /// ``` + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn pretty(mut self, pretty: bool) -> Self { + self.pretty = pretty; + self + } + + /// Display backtrace if available when using pretty output format. + /// + /// # Examples + /// + /// **Note**: Report will search for the first `Backtrace` it can find starting from the + /// outermost error. In this example it will display the backtrace from the second error in the + /// chain, `SuperErrorSideKick`. + /// + /// ```rust + /// #![feature(error_reporter)] + /// #![feature(backtrace)] + /// # use std::error::Error; + /// # use std::fmt; + /// use std::error::Report; + /// use std::backtrace::Backtrace; + /// + /// # #[derive(Debug)] + /// # struct SuperError { + /// # source: SuperErrorSideKick, + /// # } + /// # impl fmt::Display for SuperError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperError is here!") + /// # } + /// # } + /// # impl Error for SuperError { + /// # fn source(&self) -> Option<&(dyn Error + 'static)> { + /// # Some(&self.source) + /// # } + /// # } + /// #[derive(Debug)] + /// struct SuperErrorSideKick { + /// backtrace: Backtrace, + /// } + /// + /// impl SuperErrorSideKick { + /// fn new() -> SuperErrorSideKick { + /// SuperErrorSideKick { backtrace: Backtrace::force_capture() } + /// } + /// } + /// + /// impl Error for SuperErrorSideKick { + /// fn backtrace(&self) -> Option<&Backtrace> { + /// Some(&self.backtrace) + /// } + /// } + /// + /// // The rest of the example is unchanged ... + /// # impl fmt::Display for SuperErrorSideKick { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "SuperErrorSideKick is here!") + /// # } + /// # } + /// + /// let source = SuperErrorSideKick::new(); + /// let error = SuperError { source }; + /// let report = Report::new(error).pretty(true).show_backtrace(true); + /// eprintln!("Error: {:?}", report); + /// ``` + /// + /// This example produces something similar to the following output: + /// + /// ```console + /// Error: SuperError is here! + /// + /// Caused by: + /// SuperErrorSideKick is here! + /// + /// Stack backtrace: + /// 0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new + /// 1: rust_out::main::_doctest_main_src_error_rs_1158_0 + /// 2: rust_out::main + /// 3: core::ops::function::FnOnce::call_once + /// 4: std::sys_common::backtrace::__rust_begin_short_backtrace + /// 5: std::rt::lang_start::{{closure}} + /// 6: std::panicking::try + /// 7: std::rt::lang_start_internal + /// 8: std::rt::lang_start + /// 9: main + /// 10: __libc_start_main + /// 11: _start + /// ``` + #[unstable(feature = "error_reporter", issue = "90172")] + pub fn show_backtrace(mut self, show_backtrace: bool) -> Self { + self.show_backtrace = show_backtrace; + self + } +} + +impl Report +where + E: Error, +{ + fn backtrace(&self) -> Option<&Backtrace> { + // have to grab the backtrace on the first error directly since that error may not be + // 'static + let backtrace = self.error.backtrace(); + let backtrace = backtrace.or_else(|| { + self.error + .source() + .map(|source| source.chain().find_map(|source| source.backtrace())) + .flatten() + }); + backtrace + } + + /// Format the report as a single line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.error)?; + + let sources = self.error.source().into_iter().flat_map(::chain); + + for cause in sources { + write!(f, ": {}", cause)?; + } + + Ok(()) + } + + /// Format the report as multiple lines, with each error cause on its own line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let error = &self.error; + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + + let multiple = cause.source().is_some(); + + for (ind, error) in cause.chain().enumerate() { + writeln!(f)?; + let mut indented = Indented { inner: f }; + if multiple { + write!(indented, "{: >4}: {}", ind, error)?; + } else { + write!(indented, " {}", error)?; + } + } + } + + if self.show_backtrace { + let backtrace = self.backtrace(); + + if let Some(backtrace) = backtrace { + let backtrace = backtrace.to_string(); + + f.write_str("\n\nStack backtrace:\n")?; + f.write_str(backtrace.trim_end())?; + } + } + + Ok(()) + } +} + +impl Report> { + fn backtrace(&self) -> Option<&Backtrace> { + // have to grab the backtrace on the first error directly since that error may not be + // 'static + let backtrace = self.error.backtrace(); + let backtrace = backtrace.or_else(|| { + self.error + .source() + .map(|source| source.chain().find_map(|source| source.backtrace())) + .flatten() + }); + backtrace + } + + /// Format the report as a single line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.error)?; + + let sources = self.error.source().into_iter().flat_map(::chain); + + for cause in sources { + write!(f, ": {}", cause)?; + } + + Ok(()) + } + + /// Format the report as multiple lines, with each error cause on its own line. + #[unstable(feature = "error_reporter", issue = "90172")] + fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let error = &self.error; + + write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + write!(f, "\n\nCaused by:")?; + + let multiple = cause.source().is_some(); + + for (ind, error) in cause.chain().enumerate() { + writeln!(f)?; + let mut indented = Indented { inner: f }; + if multiple { + write!(indented, "{: >4}: {}", ind, error)?; + } else { + write!(indented, " {}", error)?; + } + } + } + + if self.show_backtrace { + let backtrace = self.backtrace(); + + if let Some(backtrace) = backtrace { + let backtrace = backtrace.to_string(); + + f.write_str("\n\nStack backtrace:\n")?; + f.write_str(backtrace.trim_end())?; + } + } + + Ok(()) + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl From for Report +where + E: Error, +{ + fn from(error: E) -> Self { + Report { error, show_backtrace: false, pretty: false } + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl<'a, E> From for Report> +where + E: Error + 'a, +{ + fn from(error: E) -> Self { + let error = box error; + Report { error, show_backtrace: false, pretty: false } + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl fmt::Display for Report +where + E: Error, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) } + } +} + +#[unstable(feature = "error_reporter", issue = "90172")] +impl fmt::Display for Report> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) } + } +} + +// This type intentionally outputs the same format for `Display` and `Debug`for +// situations where you unwrap a `Report` or return it from main. +#[unstable(feature = "error_reporter", issue = "90172")] +impl fmt::Debug for Report +where + Report: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/// Wrapper type for indenting the inner source. +struct Indented<'a, D> { + inner: &'a mut D, +} + +impl Write for Indented<'_, T> +where + T: Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + for (i, line) in s.split('\n').enumerate() { + if i > 0 { + self.inner.write_char('\n')?; + self.inner.write_str(" ")?; + } + + self.inner.write_str(line)?; + } + + Ok(()) + } +} diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index 66d6924f34..eae5f43ff3 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -35,3 +35,408 @@ fn downcasting() { Err(e) => assert_eq!(*e.downcast::().unwrap(), A), } } + +use crate::backtrace::Backtrace; +use crate::error::Report; + +#[derive(Debug)] +struct SuperError { + source: SuperErrorSideKick, +} + +impl fmt::Display for SuperError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SuperError is here!") + } +} + +impl Error for SuperError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.source) + } +} + +#[derive(Debug)] +struct SuperErrorSideKick; + +impl fmt::Display for SuperErrorSideKick { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SuperErrorSideKick is here!") + } +} + +impl Error for SuperErrorSideKick {} + +#[test] +fn single_line_formatting() { + let error = SuperError { source: SuperErrorSideKick }; + let report = Report::new(&error); + let actual = report.to_string(); + let expected = String::from("SuperError is here!: SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn multi_line_formatting() { + let error = SuperError { source: SuperErrorSideKick }; + let report = Report::new(&error).pretty(true); + let actual = report.to_string(); + let expected = String::from( + "\ +SuperError is here! + +Caused by: + SuperErrorSideKick is here!", + ); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_no_sources_formats_single_line_correctly() { + let report = Report::new(SuperErrorSideKick); + let actual = report.to_string(); + let expected = String::from("SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_no_sources_formats_multi_line_correctly() { + let report = Report::new(SuperErrorSideKick).pretty(true); + let actual = report.to_string(); + let expected = String::from("SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_backtrace_outputs_correctly_with_one_source() { + let trace = Backtrace::force_capture(); + let expected = format!( + "\ +The source of the error + +Caused by: + Error with backtrace + +Stack backtrace: +{}", + trace + ); + let error = GenericError::new("Error with backtrace"); + let mut error = GenericError::new_with_source("The source of the error", error); + error.backtrace = Some(trace); + let report = Report::new(error).pretty(true).show_backtrace(true); + + println!("Error: {}", report); + assert_eq!(expected.trim_end(), report.to_string()); +} + +#[test] +fn error_with_backtrace_outputs_correctly_with_two_sources() { + let trace = Backtrace::force_capture(); + let expected = format!( + "\ +Error with two sources + +Caused by: + 0: The source of the error + 1: Error with backtrace + +Stack backtrace: +{}", + trace + ); + let mut error = GenericError::new("Error with backtrace"); + error.backtrace = Some(trace); + let error = GenericError::new_with_source("The source of the error", error); + let error = GenericError::new_with_source("Error with two sources", error); + let report = Report::new(error).pretty(true).show_backtrace(true); + + println!("Error: {}", report); + assert_eq!(expected.trim_end(), report.to_string()); +} + +#[derive(Debug)] +struct GenericError { + message: D, + backtrace: Option, + source: Option>, +} + +impl GenericError { + fn new(message: D) -> GenericError { + Self { message, backtrace: None, source: None } + } + + fn new_with_source(message: D, source: E) -> GenericError + where + E: Error + 'static, + { + let source: Box = Box::new(source); + let source = Some(source); + GenericError { message, backtrace: None, source } + } +} + +impl fmt::Display for GenericError +where + D: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.message, f) + } +} + +impl Error for GenericError +where + D: fmt::Debug + fmt::Display, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.source.as_deref() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.backtrace.as_ref() + } +} + +#[test] +fn error_formats_single_line_with_rude_display_impl() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2")?; + f.write_str("\nline 3\nline 4\n")?; + f.write_str("line 5\nline 6")?; + Ok(()) + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error); + let expected = "\ +line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn error_formats_multi_line_with_rude_display_impl() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2")?; + f.write_str("\nline 3\nline 4\n")?; + f.write_str("line 5\nline 6")?; + Ok(()) + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "line 1 +line 2 +line 3 +line 4 +line 5 +line 6 + +Caused by: + 0: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 1: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 2: line 1 + line 2 + line 3 + line 4 + line 5 + line 6"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_that_start_with_newline_formats_correctly() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("\nThe message\n") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = " +The message + + +Caused by: + 0: \ +\n The message + \ +\n 1: \ +\n The message + "; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_with_multiple_writes_on_same_line_dont_insert_erroneous_newlines() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("The message")?; + f.write_str(" goes on")?; + f.write_str(" and on.") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +The message goes on and on. + +Caused by: + 0: The message goes on and on. + 1: The message goes on and on."; + + let actual = report.to_string(); + println!("{}", actual); + assert_eq!(expected, actual); +} + +#[test] +fn errors_with_string_interpolation_formats_correctly() { + #[derive(Debug)] + struct MyMessage(usize); + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Got an error code: ({}). ", self.0)?; + write!(f, "What would you like to do in response?") + } + } + + let error = GenericError::new(MyMessage(10)); + let error = GenericError::new_with_source(MyMessage(20), error); + let report = Report::new(error).pretty(true); + let expected = "\ +Got an error code: (20). What would you like to do in response? + +Caused by: + Got an error code: (10). What would you like to do in response?"; + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn empty_lines_mid_message() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\n\nline 2") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +line 1 + +line 2 + +Caused by: + 0: line 1 + \ +\n line 2 + 1: line 1 + \ +\n line 2"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn only_one_source() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +line 1 +line 2 + +Caused by: + line 1 + line 2"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 9c1b79d696..1678367290 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -373,38 +373,61 @@ impl CString { /// the position of the nul byte. #[stable(feature = "rust1", since = "1.0.0")] pub fn new>>(t: T) -> Result { - trait SpecIntoVec { - fn into_vec(self) -> Vec; + trait SpecNewImpl { + fn spec_new_impl(self) -> Result; } - impl>> SpecIntoVec for T { - default fn into_vec(self) -> Vec { - self.into() + + impl>> SpecNewImpl for T { + default fn spec_new_impl(self) -> Result { + let bytes: Vec = self.into(); + match memchr::memchr(0, &bytes) { + Some(i) => Err(NulError(i, bytes)), + None => Ok(unsafe { CString::_from_vec_unchecked(bytes) }), + } } } - // Specialization for avoiding reallocation. - impl SpecIntoVec for &'_ [u8] { - fn into_vec(self) -> Vec { - let mut v = Vec::with_capacity(self.len() + 1); - v.extend(self); - v + + // Specialization for avoiding reallocation + #[inline(always)] // Without that it is not inlined into specializations + fn spec_new_impl_bytes(bytes: &[u8]) -> Result { + // We cannot have such large slice that we would overflow here + // but using `checked_add` allows LLVM to assume that capacity never overflows + // and generate twice shorter code. + // `saturating_add` doesn't help for some reason. + let capacity = bytes.len().checked_add(1).unwrap(); + + // Allocate before validation to avoid duplication of allocation code. + // We still need to allocate and copy memory even if we get an error. + let mut buffer = Vec::with_capacity(capacity); + buffer.extend(bytes); + + // Check memory of self instead of new buffer. + // This allows better optimizations if lto enabled. + match memchr::memchr(0, bytes) { + Some(i) => Err(NulError(i, buffer)), + None => Ok(unsafe { CString::_from_vec_unchecked(buffer) }), } } - impl SpecIntoVec for &'_ str { - fn into_vec(self) -> Vec { - let mut v = Vec::with_capacity(self.len() + 1); - v.extend(self.as_bytes()); - v + + impl SpecNewImpl for &'_ [u8] { + fn spec_new_impl(self) -> Result { + spec_new_impl_bytes(self) } } - Self::_new(SpecIntoVec::into_vec(t)) - } + impl SpecNewImpl for &'_ str { + fn spec_new_impl(self) -> Result { + spec_new_impl_bytes(self.as_bytes()) + } + } - fn _new(bytes: Vec) -> Result { - match memchr::memchr(0, &bytes) { - Some(i) => Err(NulError(i, bytes)), - None => Ok(unsafe { CString::from_vec_unchecked(bytes) }), + impl SpecNewImpl for &'_ mut [u8] { + fn spec_new_impl(self) -> Result { + spec_new_impl_bytes(self) + } } + + t.spec_new_impl() } /// Creates a C-compatible string by consuming a byte vector, @@ -428,10 +451,15 @@ impl CString { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn from_vec_unchecked(mut v: Vec) -> CString { + pub unsafe fn from_vec_unchecked(v: Vec) -> Self { + debug_assert!(memchr::memchr(0, &v).is_none()); + unsafe { Self::_from_vec_unchecked(v) } + } + + unsafe fn _from_vec_unchecked(mut v: Vec) -> Self { v.reserve_exact(1); v.push(0); - CString { inner: v.into_boxed_slice() } + Self { inner: v.into_boxed_slice() } } /// Retakes ownership of a `CString` that was transferred to C via @@ -555,7 +583,7 @@ impl CString { pub fn into_string(self) -> Result { String::from_utf8(self.into_bytes()).map_err(|e| IntoStringError { error: e.utf8_error(), - inner: unsafe { CString::from_vec_unchecked(e.into_bytes()) }, + inner: unsafe { Self::_from_vec_unchecked(e.into_bytes()) }, }) } @@ -712,6 +740,11 @@ impl CString { #[must_use] #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub unsafe fn from_vec_with_nul_unchecked(v: Vec) -> Self { + debug_assert!(memchr::memchr(0, &v).unwrap() + 1 == v.len()); + unsafe { Self::_from_vec_with_nul_unchecked(v) } + } + + unsafe fn _from_vec_with_nul_unchecked(v: Vec) -> Self { Self { inner: v.into_boxed_slice() } } @@ -755,7 +788,7 @@ impl CString { Some(nul_pos) if nul_pos + 1 == v.len() => { // SAFETY: We know there is only one nul byte, at the end // of the vec. - Ok(unsafe { Self::from_vec_with_nul_unchecked(v) }) + Ok(unsafe { Self::_from_vec_with_nul_unchecked(v) }) } Some(nul_pos) => Err(FromVecWithNulError { error_kind: FromBytesWithNulErrorKind::InteriorNul(nul_pos), @@ -788,7 +821,7 @@ impl ops::Deref for CString { #[inline] fn deref(&self) -> &CStr { - unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) } + unsafe { CStr::_from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) } } } @@ -848,6 +881,8 @@ impl Borrow for CString { #[stable(feature = "cstring_from_cow_cstr", since = "1.28.0")] impl<'a> From> for CString { + /// Converts a `Cow<'a, CStr>` into a `CString`, by copying the contents if they are + /// borrowed. #[inline] fn from(s: Cow<'a, CStr>) -> Self { s.into_owned() @@ -856,6 +891,8 @@ impl<'a> From> for CString { #[stable(feature = "box_from_c_str", since = "1.17.0")] impl From<&CStr> for Box { + /// Converts a `&CStr` into a `Box`, + /// by copying the contents into a newly allocated [`Box`]. fn from(s: &CStr) -> Box { let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul()); unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) } @@ -864,6 +901,8 @@ impl From<&CStr> for Box { #[stable(feature = "box_from_cow", since = "1.45.0")] impl From> for Box { + /// Converts a `Cow<'a, CStr>` into a `Box`, + /// by copying the contents if they are borrowed. #[inline] fn from(cow: Cow<'_, CStr>) -> Box { match cow { @@ -899,7 +938,7 @@ impl From> for CString { }; // SAFETY: `v` cannot contain null bytes, given the type-level // invariant of `NonZeroU8`. - CString::from_vec_unchecked(v) + Self::_from_vec_unchecked(v) } } } @@ -950,7 +989,8 @@ impl<'a> From<&'a CString> for Cow<'a, CStr> { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { - /// Converts a [`CString`] into an [Arc]<[CStr]> without copying or allocating. + /// Converts a [`CString`] into an [Arc]<[CStr]> by moving the [`CString`] + /// data into a new [`Arc`] buffer. #[inline] fn from(s: CString) -> Arc { let arc: Arc<[u8]> = Arc::from(s.into_inner()); @@ -960,6 +1000,8 @@ impl From for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&CStr> for Arc { + /// Converts a `&CStr` into a `Arc`, + /// by copying the contents into a newly allocated [`Arc`]. #[inline] fn from(s: &CStr) -> Arc { let arc: Arc<[u8]> = Arc::from(s.to_bytes_with_nul()); @@ -969,7 +1011,8 @@ impl From<&CStr> for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Rc { - /// Converts a [`CString`] into an [Rc]<[CStr]> without copying or allocating. + /// Converts a [`CString`] into an [Rc]<[CStr]> by moving the [`CString`] + /// data into a new [`Arc`] buffer. #[inline] fn from(s: CString) -> Rc { let rc: Rc<[u8]> = Rc::from(s.into_inner()); @@ -979,6 +1022,8 @@ impl From for Rc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&CStr> for Rc { + /// Converts a `&CStr` into a `Rc`, + /// by copying the contents into a newly allocated [`Rc`]. #[inline] fn from(s: &CStr) -> Rc { let rc: Rc<[u8]> = Rc::from(s.to_bytes_with_nul()); @@ -1052,7 +1097,7 @@ impl fmt::Display for NulError { impl From for io::Error { /// Converts a [`NulError`] into a [`io::Error`]. fn from(_: NulError) -> io::Error { - io::Error::new_const(io::ErrorKind::InvalidInput, &"data provided contains a nul byte") + io::const_io_error!(io::ErrorKind::InvalidInput, "data provided contains a nul byte") } } @@ -1190,7 +1235,7 @@ impl CStr { unsafe { let len = sys::strlen(ptr); let ptr = ptr as *const u8; - CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1)) + Self::_from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1)) } } @@ -1227,15 +1272,16 @@ impl CStr { /// assert!(cstr.is_err()); /// ``` #[stable(feature = "cstr_from_bytes", since = "1.10.0")] - pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> { + pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> { let nul_pos = memchr::memchr(0, bytes); - if let Some(nul_pos) = nul_pos { - if nul_pos + 1 != bytes.len() { - return Err(FromBytesWithNulError::interior_nul(nul_pos)); + match nul_pos { + Some(nul_pos) if nul_pos + 1 == bytes.len() => { + // SAFETY: We know there is only one nul byte, at the end + // of the byte slice. + Ok(unsafe { Self::_from_bytes_with_nul_unchecked(bytes) }) } - Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }) - } else { - Err(FromBytesWithNulError::not_nul_terminated()) + Some(nul_pos) => Err(FromBytesWithNulError::interior_nul(nul_pos)), + None => Err(FromBytesWithNulError::not_nul_terminated()), } } @@ -1261,12 +1307,19 @@ impl CStr { #[stable(feature = "cstr_from_bytes", since = "1.10.0")] #[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")] pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { + // We're in a const fn, so this is the best we can do + debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); + unsafe { Self::_from_bytes_with_nul_unchecked(bytes) } + } + + #[inline] + const unsafe fn _from_bytes_with_nul_unchecked(bytes: &[u8]) -> &Self { // SAFETY: Casting to CStr is safe because its internal representation // is a [u8] too (safe only inside std). // Dereferencing the obtained pointer is safe because it comes from a // reference. Making a reference is then safe because its lifetime // is bound by the lifetime of the given `bytes`. - unsafe { &*(bytes as *const [u8] as *const CStr) } + unsafe { &*(bytes as *const [u8] as *const Self) } } /// Returns the inner pointer to this C string. @@ -1504,6 +1557,7 @@ impl ToOwned for CStr { #[stable(feature = "cstring_asref", since = "1.7.0")] impl From<&CStr> for CString { + /// Copies the contents of the `&CStr` into a newly allocated `CString`. fn from(s: &CStr) -> CString { s.to_owned() } @@ -1529,7 +1583,7 @@ impl ops::Index> for CStr { // byte, since otherwise we could get an empty string that doesn't end // in a null. if index.start < bytes.len() { - unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[index.start..]) } + unsafe { CStr::_from_bytes_with_nul_unchecked(&bytes[index.start..]) } } else { panic!( "index out of bounds: the len is {} but the index is {}", diff --git a/library/std/src/ffi/c_str/tests.rs b/library/std/src/ffi/c_str/tests.rs index 4f7ba9ad43..00ba546082 100644 --- a/library/std/src/ffi/c_str/tests.rs +++ b/library/std/src/ffi/c_str/tests.rs @@ -32,14 +32,6 @@ fn build_with_zero2() { assert!(CString::new(vec![0]).is_err()); } -#[test] -fn build_with_zero3() { - unsafe { - let s = CString::from_vec_unchecked(vec![0]); - assert_eq!(s.as_bytes(), b"\0"); - } -} - #[test] fn formatted() { let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap(); diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 982ad18987..9b5e5d6c0c 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -452,6 +452,8 @@ impl From for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl> From<&T> for OsString { + /// Copies any value implementing [AsRef]<[OsStr]> + /// into a newly allocated [`OsString`]. fn from(s: &T) -> OsString { s.as_ref().to_os_string() } @@ -942,6 +944,7 @@ impl OsStr { #[stable(feature = "box_from_os_str", since = "1.17.0")] impl From<&OsStr> for Box { + /// Copies the string into a newly allocated [Box]<[OsStr]>. #[inline] fn from(s: &OsStr) -> Box { let rw = Box::into_raw(s.inner.into_box()) as *mut OsStr; @@ -951,6 +954,8 @@ impl From<&OsStr> for Box { #[stable(feature = "box_from_cow", since = "1.45.0")] impl From> for Box { + /// Converts a `Cow<'a, OsStr>` into a [Box]<[OsStr]>, + /// by copying the contents if they are borrowed. #[inline] fn from(cow: Cow<'_, OsStr>) -> Box { match cow { @@ -989,7 +994,8 @@ impl Clone for Box { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { - /// Converts an [`OsString`] into an [Arc]<[OsStr]> without copying or allocating. + /// Converts an [`OsString`] into an [Arc]<[OsStr]> by moving the [`OsString`] + /// data into a new [`Arc`] buffer. #[inline] fn from(s: OsString) -> Arc { let arc = s.inner.into_arc(); @@ -999,6 +1005,7 @@ impl From for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&OsStr> for Arc { + /// Copies the string into a newly allocated [Arc]<[OsStr]>. #[inline] fn from(s: &OsStr) -> Arc { let arc = s.inner.into_arc(); @@ -1008,7 +1015,8 @@ impl From<&OsStr> for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Rc { - /// Converts an [`OsString`] into an [Rc]<[OsStr]> without copying or allocating. + /// Converts an [`OsString`] into an [Rc]<[OsStr]> by moving the [`OsString`] + /// data into a new [`Rc`] buffer. #[inline] fn from(s: OsString) -> Rc { let rc = s.inner.into_rc(); @@ -1018,6 +1026,7 @@ impl From for Rc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&OsStr> for Rc { + /// Copies the string into a newly allocated [Rc]<[OsStr]>. #[inline] fn from(s: &OsStr) -> Rc { let rc = s.inner.into_rc(); @@ -1027,6 +1036,7 @@ impl From<&OsStr> for Rc { #[stable(feature = "cow_from_osstr", since = "1.28.0")] impl<'a> From for Cow<'a, OsStr> { + /// Moves the string into a [`Cow::Owned`]. #[inline] fn from(s: OsString) -> Cow<'a, OsStr> { Cow::Owned(s) @@ -1035,6 +1045,7 @@ impl<'a> From for Cow<'a, OsStr> { #[stable(feature = "cow_from_osstr", since = "1.28.0")] impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { + /// Converts the string reference into a [`Cow::Borrowed`]. #[inline] fn from(s: &'a OsStr) -> Cow<'a, OsStr> { Cow::Borrowed(s) @@ -1043,6 +1054,7 @@ impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { #[stable(feature = "cow_from_osstr", since = "1.28.0")] impl<'a> From<&'a OsString> for Cow<'a, OsStr> { + /// Converts the string reference into a [`Cow::Borrowed`]. #[inline] fn from(s: &'a OsString) -> Cow<'a, OsStr> { Cow::Borrowed(s.as_os_str()) @@ -1051,6 +1063,8 @@ impl<'a> From<&'a OsString> for Cow<'a, OsStr> { #[stable(feature = "osstring_from_cow_osstr", since = "1.28.0")] impl<'a> From> for OsString { + /// Converts a `Cow<'a, OsStr>` into an [`OsString`], + /// by copying the contents if they are borrowed. #[inline] fn from(s: Cow<'a, OsStr>) -> Self { s.into_owned() diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 0e1363328c..0b65336a5a 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -356,9 +356,10 @@ impl File { /// open or create a file with specific options if `open()` or `create()` /// are not appropriate. /// - /// It is equivalent to `OpenOptions::new()` but allows you to write more - /// readable code. Instead of `OpenOptions::new().read(true).open("foo.txt")` - /// you can write `File::options().read(true).open("foo.txt")`. This + /// It is equivalent to `OpenOptions::new()`, but allows you to write more + /// readable code. Instead of + /// `OpenOptions::new().append(true).open("example.log")`, + /// you can write `File::options().append(true).open("example.log")`. This /// also avoids the need to import `OpenOptions`. /// /// See the [`OpenOptions::new`] function for more details. @@ -369,7 +370,7 @@ impl File { /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { - /// let mut f = File::options().read(true).open("foo.txt")?; + /// let mut f = File::options().append(true).open("example.log")?; /// Ok(()) /// } /// ``` @@ -1049,7 +1050,7 @@ impl Metadata { /// /// fn main() -> std::io::Result<()> { /// let link_path = Path::new("link"); - /// symlink("/origin_does_not_exists/", link_path)?; + /// symlink("/origin_does_not_exist/", link_path)?; /// /// let metadata = fs::symlink_metadata(link_path)?; /// @@ -2043,7 +2044,7 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// /// This function currently corresponds to `openat`, `fdopendir`, `unlinkat` and `lstat` functions /// on Unix (except for macOS before version 10.10 and REDOX) and the `CreateFileW`, -/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtOpenFile` functions on +/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile` functions on /// Windows. Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -2262,9 +2263,9 @@ impl DirBuilder { match path.parent() { Some(p) => self.create_dir_all(p)?, None => { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::Uncategorized, - &"failed to create whole tree", + "failed to create whole tree", )); } } @@ -2287,7 +2288,7 @@ impl AsInnerMut for DirBuilder { /// This function will traverse symbolic links to query information about the /// destination file. In case of broken symbolic links this will return `Ok(false)`. /// -/// As opposed to the `exists()` method, this one doesn't silently ignore errors +/// As opposed to the [`Path::exists`] method, this one doesn't silently ignore errors /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission /// denied on some of the parent directories.) /// @@ -2300,6 +2301,8 @@ impl AsInnerMut for DirBuilder { /// assert!(!fs::try_exists("does_not_exist.txt").expect("Can't check existence of file does_not_exist.txt")); /// assert!(fs::try_exists("/root/secret_file.txt").is_err()); /// ``` +/// +/// [`Path::exists`]: crate::path::Path::exists // FIXME: stabilization should modify documentation of `exists()` to recommend this method // instead. #[unstable(feature = "path_try_exists", issue = "83186")] diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index a62c01ef29..16b8bf6824 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1504,3 +1504,19 @@ fn create_dir_long_paths() { let path = Path::new(""); assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound); } + +/// Ensure ReadDir works on large directories. +/// Regression test for https://github.com/rust-lang/rust/issues/93384. +#[test] +fn read_large_dir() { + let tmpdir = tmpdir(); + + let count = 32 * 1024; + for i in 0..count { + check!(fs::File::create(tmpdir.join(&i.to_string()))); + } + + for entry in fs::read_dir(tmpdir.path()).unwrap() { + entry.unwrap(); + } +} diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index b56dc65f0b..e7eee44362 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -357,9 +357,9 @@ impl Read for BufReader { let mut bytes = Vec::new(); self.read_to_end(&mut bytes)?; let string = crate::str::from_utf8(&bytes).map_err(|_| { - io::Error::new_const( + io::const_io_error!( io::ErrorKind::InvalidData, - &"stream did not contain valid UTF-8", + "stream did not contain valid UTF-8", ) })?; *buf += string; diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index c7423e4d92..2d3a0f37b4 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -1,7 +1,7 @@ use crate::error; use crate::fmt; use crate::io::{ - self, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, + self, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, }; use crate::mem; use crate::ptr; @@ -168,9 +168,9 @@ impl BufWriter { match r { Ok(0) => { - return Err(Error::new_const( + return Err(io::const_io_error!( ErrorKind::WriteZero, - &"failed to write the buffered data", + "failed to write the buffered data", )); } Ok(n) => guard.consume(n), diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs index 179bdf7fe5..100dab1e24 100644 --- a/library/std/src/io/buffered/mod.rs +++ b/library/std/src/io/buffered/mod.rs @@ -12,12 +12,12 @@ use crate::error; use crate::fmt; use crate::io::Error; -pub use bufreader::BufReader; -pub use bufwriter::BufWriter; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter}; +use linewritershim::LineWriterShim; + #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use bufwriter::WriterPanicked; -pub use linewriter::LineWriter; -use linewritershim::LineWriterShim; /// An error returned by [`BufWriter::into_inner`] which combines an error that /// happened while writing out the buffer, and the buffered writer object diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 416cc906e6..fc19704bec 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,7 +4,7 @@ mod tests; use crate::io::prelude::*; use crate::cmp; -use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; +use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; use core::convert::TryInto; @@ -297,9 +297,9 @@ where self.pos = n; Ok(self.pos) } - None => Err(Error::new_const( + None => Err(io::const_io_error!( ErrorKind::InvalidInput, - &"invalid seek to a negative or overflowing position", + "invalid seek to a negative or overflowing position", )), } } @@ -400,9 +400,9 @@ fn slice_write_vectored( // Resizing write implementation fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result { let pos: usize = (*pos_mut).try_into().map_err(|_| { - Error::new_const( + io::const_io_error!( ErrorKind::InvalidInput, - &"cursor position exceeds maximum possible vector length", + "cursor position exceeds maximum possible vector length", ) })?; // Make sure the internal buffer is as least as big as where we diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 210a9ec718..1aa6d65788 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,6 +1,16 @@ #[cfg(test)] mod tests; +#[cfg(target_pointer_width = "64")] +mod repr_bitpacked; +#[cfg(target_pointer_width = "64")] +use repr_bitpacked::Repr; + +#[cfg(not(target_pointer_width = "64"))] +mod repr_unpacked; +#[cfg(not(target_pointer_width = "64"))] +use repr_unpacked::Repr; + use crate::convert::From; use crate::error; use crate::fmt; @@ -66,15 +76,58 @@ impl fmt::Debug for Error { } } -enum Repr { +// Only derive debug in tests, to make sure it +// doesn't accidentally get printed. +#[cfg_attr(test, derive(Debug))] +enum ErrorData { Os(i32), Simple(ErrorKind), - // &str is a fat pointer, but &&str is a thin pointer. - SimpleMessage(ErrorKind, &'static &'static str), - Custom(Box), + SimpleMessage(&'static SimpleMessage), + Custom(C), +} + +// `#[repr(align(4))]` is probably redundant, it should have that value or +// higher already. We include it just because repr_bitpacked.rs's encoding +// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the +// alignment required by the struct, only increase it). +// +// If we add more variants to ErrorData, this can be increased to 8, but it +// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or +// whatever cfg we're using to enable the `repr_bitpacked` code, since only the +// that version needs the alignment, and 8 is higher than the alignment we'll +// have on 32 bit platforms. +// +// (For the sake of being explicit: the alignment requirement here only matters +// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't +// matter at all) +#[repr(align(4))] +#[derive(Debug)] +pub(crate) struct SimpleMessage { + kind: ErrorKind, + message: &'static str, } +impl SimpleMessage { + pub(crate) const fn new(kind: ErrorKind, message: &'static str) -> Self { + Self { kind, message } + } +} + +/// Create and return an `io::Error` for a given `ErrorKind` and constant +/// message. This doesn't allocate. +pub(crate) macro const_io_error($kind:expr, $message:expr $(,)?) { + $crate::io::error::Error::from_static_message({ + const MESSAGE_DATA: $crate::io::error::SimpleMessage = + $crate::io::error::SimpleMessage::new($kind, $message); + &MESSAGE_DATA + }) +} + +// As with `SimpleMessage`: `#[repr(align(4))]` here is just because +// repr_bitpacked's encoding requires it. In practice it almost certainly be +// already be this high or higher. #[derive(Debug)] +#[repr(align(4))] struct Custom { kind: ErrorKind, error: Box, @@ -243,12 +296,11 @@ pub enum ErrorKind { /// The filesystem does not support making so many hardlinks to the same file. #[unstable(feature = "io_error_more", issue = "86442")] TooManyLinks, - /// Filename too long. + /// A filename was invalid. /// - /// The limit might be from the underlying filesystem or API, or an administratively imposed - /// resource limit. + /// This error can also cause if it exceeded the filename length limit. #[unstable(feature = "io_error_more", issue = "86442")] - FilenameTooLong, + InvalidFilename, /// Program argument list too long. /// /// When trying to run an external program, a system or process limit on the size of the @@ -329,12 +381,12 @@ impl ErrorKind { DirectoryNotEmpty => "directory not empty", ExecutableFileBusy => "executable file busy", FileTooLarge => "file too large", - FilenameTooLong => "filename too long", FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", FilesystemQuotaExceeded => "filesystem quota exceeded", HostUnreachable => "host unreachable", Interrupted => "operation interrupted", InvalidData => "invalid data", + InvalidFilename => "invalid filename", InvalidInput => "invalid input parameter", IsADirectory => "is a directory", NetworkDown => "network down", @@ -361,13 +413,29 @@ impl ErrorKind { } } +#[stable(feature = "io_errorkind_display", since = "1.60.0")] +impl fmt::Display for ErrorKind { + /// Shows a human-readable description of the `ErrorKind`. + /// + /// This is similar to `impl Display for Error`, but doesn't require first converting to Error. + /// + /// # Examples + /// ``` + /// use std::io::ErrorKind; + /// assert_eq!("entity not found", ErrorKind::NotFound.to_string()); + /// ``` + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.as_str()) + } +} + /// Intended for use for errors not exposed to the user, where allocating onto /// the heap (for normal construction via Error::new) is too costly. #[stable(feature = "io_error_from_errorkind", since = "1.14.0")] impl From for Error { /// Converts an [`ErrorKind`] into an [`Error`]. /// - /// This conversion allocates a new error with a simple representation of error kind. + /// This conversion creates a new error with a simple representation of error kind. /// /// # Examples /// @@ -380,7 +448,7 @@ impl From for Error { /// ``` #[inline] fn from(kind: ErrorKind) -> Error { - Error { repr: Repr::Simple(kind) } + Error { repr: Repr::new_simple(kind) } } } @@ -445,20 +513,22 @@ impl Error { } fn _new(kind: ErrorKind, error: Box) -> Error { - Error { repr: Repr::Custom(Box::new(Custom { kind, error })) } + Error { repr: Repr::new_custom(Box::new(Custom { kind, error })) } } - /// Creates a new I/O error from a known kind of error as well as a - /// constant message. + /// Creates a new I/O error from a known kind of error as well as a constant + /// message. /// /// This function does not allocate. /// - /// This function should maybe change to - /// `new_const(kind: ErrorKind)` - /// in the future, when const generics allow that. + /// You should not use this directly, and instead use the `const_io_error!` + /// macro: `io::const_io_error!(ErrorKind::Something, "some_message")`. + /// + /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. #[inline] - pub(crate) const fn new_const(kind: ErrorKind, message: &'static &'static str) -> Error { - Self { repr: Repr::SimpleMessage(kind, message) } + pub(crate) const fn from_static_message(msg: &'static SimpleMessage) -> Error { + Self { repr: Repr::new_simple_message(msg) } } /// Returns an error representing the last OS error which occurred. @@ -516,7 +586,7 @@ impl Error { #[must_use] #[inline] pub fn from_raw_os_error(code: i32) -> Error { - Error { repr: Repr::Os(code) } + Error { repr: Repr::new_os(code) } } /// Returns the OS error that this error represents (if any). @@ -552,11 +622,11 @@ impl Error { #[must_use] #[inline] pub fn raw_os_error(&self) -> Option { - match self.repr { - Repr::Os(i) => Some(i), - Repr::Custom(..) => None, - Repr::Simple(..) => None, - Repr::SimpleMessage(..) => None, + match self.repr.data() { + ErrorData::Os(i) => Some(i), + ErrorData::Custom(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, } } @@ -591,11 +661,11 @@ impl Error { #[must_use] #[inline] pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::SimpleMessage(..) => None, - Repr::Custom(ref c) => Some(&*c.error), + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(&*c.error), } } @@ -665,11 +735,11 @@ impl Error { #[must_use] #[inline] pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::SimpleMessage(..) => None, - Repr::Custom(ref mut c) => Some(&mut *c.error), + match self.repr.data_mut() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(&mut *c.error), } } @@ -704,11 +774,11 @@ impl Error { #[must_use = "`self` will be dropped if the result is not used"] #[inline] pub fn into_inner(self) -> Option> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::SimpleMessage(..) => None, - Repr::Custom(c) => Some(c.error), + match self.repr.into_data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error), } } @@ -734,29 +804,31 @@ impl Error { #[must_use] #[inline] pub fn kind(&self) -> ErrorKind { - match self.repr { - Repr::Os(code) => sys::decode_error_kind(code), - Repr::Custom(ref c) => c.kind, - Repr::Simple(kind) => kind, - Repr::SimpleMessage(kind, _) => kind, + match self.repr.data() { + ErrorData::Os(code) => sys::decode_error_kind(code), + ErrorData::Custom(c) => c.kind, + ErrorData::Simple(kind) => kind, + ErrorData::SimpleMessage(m) => m.kind, } } } impl fmt::Debug for Repr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Repr::Os(code) => fmt + match self.data() { + ErrorData::Os(code) => fmt .debug_struct("Os") .field("code", &code) .field("kind", &sys::decode_error_kind(code)) .field("message", &sys::os::error_string(code)) .finish(), - Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt), - Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), - Repr::SimpleMessage(kind, &message) => { - fmt.debug_struct("Error").field("kind", &kind).field("message", &message).finish() - } + ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), + ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + ErrorData::SimpleMessage(msg) => fmt + .debug_struct("Error") + .field("kind", &msg.kind) + .field("message", &msg.message) + .finish(), } } } @@ -764,14 +836,14 @@ impl fmt::Debug for Repr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.repr { - Repr::Os(code) => { + match self.repr.data() { + ErrorData::Os(code) => { let detail = sys::os::error_string(code); write!(fmt, "{} (os error {})", detail, code) } - Repr::Custom(ref c) => c.error.fmt(fmt), - Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()), - Repr::SimpleMessage(_, &msg) => msg.fmt(fmt), + ErrorData::Custom(ref c) => c.error.fmt(fmt), + ErrorData::Simple(kind) => write!(fmt, "{}", kind.as_str()), + ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), } } } @@ -780,29 +852,29 @@ impl fmt::Display for Error { impl error::Error for Error { #[allow(deprecated, deprecated_in_future)] fn description(&self) -> &str { - match self.repr { - Repr::Os(..) | Repr::Simple(..) => self.kind().as_str(), - Repr::SimpleMessage(_, &msg) => msg, - Repr::Custom(ref c) => c.error.description(), + match self.repr.data() { + ErrorData::Os(..) | ErrorData::Simple(..) => self.kind().as_str(), + ErrorData::SimpleMessage(msg) => msg.message, + ErrorData::Custom(c) => c.error.description(), } } #[allow(deprecated)] fn cause(&self) -> Option<&dyn error::Error> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::SimpleMessage(..) => None, - Repr::Custom(ref c) => c.error.cause(), + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error.cause(), } } fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.repr { - Repr::Os(..) => None, - Repr::Simple(..) => None, - Repr::SimpleMessage(..) => None, - Repr::Custom(ref c) => c.error.source(), + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error.source(), } } } diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs new file mode 100644 index 0000000000..17d07ff566 --- /dev/null +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -0,0 +1,401 @@ +//! This is a densely packed error representation which is used on targets with +//! 64-bit pointers. +//! +//! (Note that `bitpacked` vs `unpacked` here has no relationship to +//! `#[repr(packed)]`, it just refers to attempting to use any available bits in +//! a more clever manner than `rustc`'s default layout algorithm would). +//! +//! Conceptually, it stores the same data as the "unpacked" equivalent we use on +//! other targets. Specifically, you can imagine it as an optimized version of +//! the following enum (which is roughly equivalent to what's stored by +//! `repr_unpacked::Repr`, e.g. `super::ErrorData>`): +//! +//! ```ignore (exposition-only) +//! enum ErrorData { +//! Os(i32), +//! Simple(ErrorKind), +//! SimpleMessage(&'static SimpleMessage), +//! Custom(Box), +//! } +//! ``` +//! +//! However, it packs this data into a 64bit non-zero value. +//! +//! This optimization not only allows `io::Error` to occupy a single pointer, +//! but improves `io::Result` as well, especially for situations like +//! `io::Result<()>` (which is now 64 bits) or `io::Result` (which is now +//! 128 bits), which are quite common. +//! +//! # Layout +//! Tagged values are 64 bits, with the 2 least significant bits used for the +//! tag. This means there are there are 4 "variants": +//! +//! - **Tag 0b00**: The first variant is equivalent to +//! `ErrorData::SimpleMessage`, and holds a `&'static SimpleMessage` directly. +//! +//! `SimpleMessage` has an alignment >= 4 (which is requested with +//! `#[repr(align)]` and checked statically at the bottom of this file), which +//! means every `&'static SimpleMessage` should have the both tag bits as 0, +//! meaning its tagged and untagged representation are equivalent. +//! +//! This means we can skip tagging it, which is necessary as this variant can +//! be constructed from a `const fn`, which probably cannot tag pointers (or +//! at least it would be difficult). +//! +//! - **Tag 0b01**: The other pointer variant holds the data for +//! `ErrorData::Custom` and the remaining 62 bits are used to store a +//! `Box`. `Custom` also has alignment >= 4, so the bottom two bits +//! are free to use for the tag. +//! +//! The only important thing to note is that `ptr::wrapping_add` and +//! `ptr::wrapping_sub` are used to tag the pointer, rather than bitwise +//! operations. This should preserve the pointer's provenance, which would +//! otherwise be lost. +//! +//! - **Tag 0b10**: Holds the data for `ErrorData::Os(i32)`. We store the `i32` +//! in the pointer's most significant 32 bits, and don't use the bits `2..32` +//! for anything. Using the top 32 bits is just to let us easily recover the +//! `i32` code with the correct sign. +//! +//! - **Tag 0b11**: Holds the data for `ErrorData::Simple(ErrorKind)`. This +//! stores the `ErrorKind` in the top 32 bits as well, although it doesn't +//! occupy nearly that many. Most of the bits are unused here, but it's not +//! like we need them for anything else yet. +//! +//! # Use of `NonNull<()>` +//! +//! Everything is stored in a `NonNull<()>`, which is odd, but actually serves a +//! purpose. +//! +//! Conceptually you might think of this more like: +//! +//! ```ignore (exposition-only) +//! union Repr { +//! // holds integer (Simple/Os) variants, and +//! // provides access to the tag bits. +//! bits: NonZeroU64, +//! // Tag is 0, so this is stored untagged. +//! msg: &'static SimpleMessage, +//! // Tagged (offset) `Box` pointer. +//! tagged_custom: NonNull<()>, +//! } +//! ``` +//! +//! But there are a few problems with this: +//! +//! 1. Union access is equivalent to a transmute, so this representation would +//! require we transmute between integers and pointers in at least one +//! direction, which may be UB (and even if not, it is likely harder for a +//! compiler to reason about than explicit ptr->int operations). +//! +//! 2. Even if all fields of a union have a niche, the union itself doesn't, +//! although this may change in the future. This would make things like +//! `io::Result<()>` and `io::Result` larger, which defeats part of +//! the motivation of this bitpacking. +//! +//! Storing everything in a `NonZeroUsize` (or some other integer) would be a +//! bit more traditional for pointer tagging, but it would lose provenance +//! information, couldn't be constructed from a `const fn`, and would probably +//! run into other issues as well. +//! +//! The `NonNull<()>` seems like the only alternative, even if it's fairly odd +//! to use a pointer type to store something that may hold an integer, some of +//! the time. + +use super::{Custom, ErrorData, ErrorKind, SimpleMessage}; +use alloc::boxed::Box; +use core::marker::PhantomData; +use core::mem::{align_of, size_of}; +use core::ptr::NonNull; + +// The 2 least-significant bits are used as tag. +const TAG_MASK: usize = 0b11; +const TAG_SIMPLE_MESSAGE: usize = 0b00; +const TAG_CUSTOM: usize = 0b01; +const TAG_OS: usize = 0b10; +const TAG_SIMPLE: usize = 0b11; + +/// The internal representation. +/// +/// See the module docs for more, this is just a way to hack in a check that we +/// indeed are not unwind-safe. +/// +/// ```compile_fail,E0277 +/// fn is_unwind_safe() {} +/// is_unwind_safe::(); +/// ``` +#[repr(transparent)] +pub(super) struct Repr(NonNull<()>, PhantomData>>); + +// All the types `Repr` stores internally are Send + Sync, and so is it. +unsafe impl Send for Repr {} +unsafe impl Sync for Repr {} + +impl Repr { + pub(super) fn new_custom(b: Box) -> Self { + let p = Box::into_raw(b).cast::(); + // Should only be possible if an allocator handed out a pointer with + // wrong alignment. + debug_assert_eq!((p as usize & TAG_MASK), 0); + // Note: We know `TAG_CUSTOM <= size_of::()` (static_assert at + // end of file), and both the start and end of the expression must be + // valid without address space wraparound due to `Box`'s semantics. + // + // This means it would be correct to implement this using `ptr::add` + // (rather than `ptr::wrapping_add`), but it's unclear this would give + // any benefit, so we just use `wrapping_add` instead. + let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>(); + // Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, + // because `p`'s alignment means it isn't allowed to have any of the + // `TAG_BITS` set (you can verify that addition and bitwise-or are the + // same when the operands have no bits in common using a truth table). + // + // Then, `TAG_CUSTOM | p` is not zero, as that would require + // `TAG_CUSTOM` and `p` both be zero, and neither is (as `p` came from a + // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore, + // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the + // `new_unchecked` is safe. + let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData); + // quickly smoke-check we encoded the right thing (This generally will + // only run in libstd's tests, unless the user uses -Zbuild-std) + debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed"); + res + } + + #[inline] + pub(super) fn new_os(code: i32) -> Self { + let utagged = ((code as usize) << 32) | TAG_OS; + // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. + let res = Self(unsafe { NonNull::new_unchecked(utagged as *mut ()) }, PhantomData); + // quickly smoke-check we encoded the right thing (This generally will + // only run in libstd's tests, unless the user uses -Zbuild-std) + debug_assert!( + matches!(res.data(), ErrorData::Os(c) if c == code), + "repr(os) encoding failed for {}", + code, + ); + res + } + + #[inline] + pub(super) fn new_simple(kind: ErrorKind) -> Self { + let utagged = ((kind as usize) << 32) | TAG_SIMPLE; + // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. + let res = Self(unsafe { NonNull::new_unchecked(utagged as *mut ()) }, PhantomData); + // quickly smoke-check we encoded the right thing (This generally will + // only run in libstd's tests, unless the user uses -Zbuild-std) + debug_assert!( + matches!(res.data(), ErrorData::Simple(k) if k == kind), + "repr(simple) encoding failed {:?}", + kind, + ); + res + } + + #[inline] + pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { + // Safety: References are never null. + Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) + } + + #[inline] + pub(super) fn data(&self) -> ErrorData<&Custom> { + // Safety: We're a Repr, decode_repr is fine. + unsafe { decode_repr(self.0, |c| &*c) } + } + + #[inline] + pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { + // Safety: We're a Repr, decode_repr is fine. + unsafe { decode_repr(self.0, |c| &mut *c) } + } + + #[inline] + pub(super) fn into_data(self) -> ErrorData> { + let this = core::mem::ManuallyDrop::new(self); + // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // safe because we prevent double-drop using `ManuallyDrop`. + unsafe { decode_repr(this.0, |p| Box::from_raw(p)) } + } +} + +impl Drop for Repr { + #[inline] + fn drop(&mut self) { + // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // safe because we're being dropped. + unsafe { + let _ = decode_repr(self.0, |p| Box::::from_raw(p)); + } + } +} + +// Shared helper to decode a `Repr`'s internal pointer into an ErrorData. +// +// Safety: `ptr`'s bits should be encoded as described in the document at the +// top (it should `some_repr.0`) +#[inline] +unsafe fn decode_repr(ptr: NonNull<()>, make_custom: F) -> ErrorData +where + F: FnOnce(*mut Custom) -> C, +{ + let bits = ptr.as_ptr() as usize; + match bits & TAG_MASK { + TAG_OS => { + let code = ((bits as i64) >> 32) as i32; + ErrorData::Os(code) + } + TAG_SIMPLE => { + let kind_bits = (bits >> 32) as u32; + let kind = kind_from_prim(kind_bits).unwrap_or_else(|| { + debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits); + // This means the `ptr` passed in was not valid, which violates + // the unsafe contract of `decode_repr`. + // + // Using this rather than unwrap meaningfully improves the code + // for callers which only care about one variant (usually + // `Custom`) + core::hint::unreachable_unchecked(); + }); + ErrorData::Simple(kind) + } + TAG_SIMPLE_MESSAGE => ErrorData::SimpleMessage(&*ptr.cast::().as_ptr()), + TAG_CUSTOM => { + // It would be correct for us to use `ptr::sub` here (see the + // comment above the `wrapping_add` call in `new_custom` for why), + // but it isn't clear that it makes a difference, so we don't. + let custom = ptr.as_ptr().cast::().wrapping_sub(TAG_CUSTOM).cast::(); + ErrorData::Custom(make_custom(custom)) + } + _ => { + // Can't happen, and compiler can tell + unreachable!(); + } + } +} + +// This compiles to the same code as the check+transmute, but doesn't require +// unsafe, or to hard-code max ErrorKind or its size in a way the compiler +// couldn't verify. +#[inline] +fn kind_from_prim(ek: u32) -> Option { + macro_rules! from_prim { + ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ + // Force a compile error if the list gets out of date. + const _: fn(e: $Enum) = |e: $Enum| match e { + $($Enum::$Variant => ()),* + }; + match $prim { + $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)* + _ => None, + } + }} + } + from_prim!(ek => ErrorKind { + NotFound, + PermissionDenied, + ConnectionRefused, + ConnectionReset, + HostUnreachable, + NetworkUnreachable, + ConnectionAborted, + NotConnected, + AddrInUse, + AddrNotAvailable, + NetworkDown, + BrokenPipe, + AlreadyExists, + WouldBlock, + NotADirectory, + IsADirectory, + DirectoryNotEmpty, + ReadOnlyFilesystem, + FilesystemLoop, + StaleNetworkFileHandle, + InvalidInput, + InvalidData, + TimedOut, + WriteZero, + StorageFull, + NotSeekable, + FilesystemQuotaExceeded, + FileTooLarge, + ResourceBusy, + ExecutableFileBusy, + Deadlock, + CrossesDevices, + TooManyLinks, + InvalidFilename, + ArgumentListTooLong, + Interrupted, + Other, + UnexpectedEof, + Unsupported, + OutOfMemory, + Uncategorized, + }) +} + +// Some static checking to alert us if a change breaks any of the assumptions +// that our encoding relies on for correctness and soundness. (Some of these are +// a bit overly thorough/cautious, admittedly) +// +// If any of these are hit on a platform that libstd supports, we should likely +// just use `repr_unpacked.rs` there instead (unless the fix is easy). +macro_rules! static_assert { + ($condition:expr) => { + const _: () = assert!($condition); + }; + (@usize_eq: $lhs:expr, $rhs:expr) => { + const _: [(); $lhs] = [(); $rhs]; + }; +} + +// The bitpacking we use requires pointers be exactly 64 bits. +static_assert!(@usize_eq: size_of::>(), 8); + +// We also require pointers and usize be the same size. +static_assert!(@usize_eq: size_of::>(), size_of::()); + +// `Custom` and `SimpleMessage` need to be thin pointers. +static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8); +static_assert!(@usize_eq: size_of::>(), 8); + +static_assert!((TAG_MASK + 1).is_power_of_two()); +// And they must have sufficient alignment. +static_assert!(align_of::() >= TAG_MASK + 1); +static_assert!(align_of::() >= TAG_MASK + 1); + +static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE_MESSAGE), TAG_SIMPLE_MESSAGE); +static_assert!(@usize_eq: (TAG_MASK & TAG_CUSTOM), TAG_CUSTOM); +static_assert!(@usize_eq: (TAG_MASK & TAG_OS), TAG_OS); +static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE), TAG_SIMPLE); + +// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we +// offset a pointer by this value, and expect it to both be within the same +// object, and to not wrap around the address space. See the comment in that +// function for further details. +// +// Actually, at the moment we use `ptr::wrapping_add`, not `ptr::add`, so this +// check isn't needed for that one, although the assertion that we don't +// actually wrap around in that wrapping_add does simplify the safety reasoning +// elsewhere considerably. +static_assert!(size_of::() >= TAG_CUSTOM); + +// These two store a payload which is allowed to be zero, so they must be +// non-zero to preserve the `NonNull`'s range invariant. +static_assert!(TAG_OS != 0); +static_assert!(TAG_SIMPLE != 0); +// We can't tag `SimpleMessage`s, the tag must be 0. +static_assert!(@usize_eq: TAG_SIMPLE_MESSAGE, 0); + +// Check that the point of all of this still holds. +// +// We'd check against `io::Error`, but *technically* it's allowed to vary, +// as it's not `#[repr(transparent)]`/`#[repr(C)]`. We could add that, but +// the `#[repr()]` would show up in rustdoc, which might be seen as a stable +// commitment. +static_assert!(@usize_eq: size_of::(), 8); +static_assert!(@usize_eq: size_of::>(), 8); +static_assert!(@usize_eq: size_of::>(), 8); +static_assert!(@usize_eq: size_of::>(), 16); diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/std/src/io/error/repr_unpacked.rs new file mode 100644 index 0000000000..3729c039c4 --- /dev/null +++ b/library/std/src/io/error/repr_unpacked.rs @@ -0,0 +1,50 @@ +//! This is a fairly simple unpacked error representation that's used on +//! non-64bit targets, where the packed 64 bit representation wouldn't work, and +//! would have no benefit. + +use super::{Custom, ErrorData, ErrorKind, SimpleMessage}; +use alloc::boxed::Box; + +type Inner = ErrorData>; + +pub(super) struct Repr(Inner); + +impl Repr { + pub(super) fn new_custom(b: Box) -> Self { + Self(Inner::Custom(b)) + } + #[inline] + pub(super) fn new_os(code: i32) -> Self { + Self(Inner::Os(code)) + } + #[inline] + pub(super) fn new_simple(kind: ErrorKind) -> Self { + Self(Inner::Simple(kind)) + } + #[inline] + pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { + Self(Inner::SimpleMessage(m)) + } + #[inline] + pub(super) fn into_data(self) -> ErrorData> { + self.0 + } + #[inline] + pub(super) fn data(&self) -> ErrorData<&Custom> { + match &self.0 { + Inner::Os(c) => ErrorData::Os(*c), + Inner::Simple(k) => ErrorData::Simple(*k), + Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), + Inner::Custom(m) => ErrorData::Custom(&*m), + } + } + #[inline] + pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { + match &mut self.0 { + Inner::Os(c) => ErrorData::Os(*c), + Inner::Simple(k) => ErrorData::Simple(*k), + Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), + Inner::Custom(m) => ErrorData::Custom(&mut *m), + } + } +} diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 5098a46313..c2c51553b2 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,4 +1,5 @@ -use super::{Custom, Error, ErrorKind, Repr}; +use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr}; +use crate::assert_matches::assert_matches; use crate::error; use crate::fmt; use crate::mem::size_of; @@ -16,9 +17,9 @@ fn test_debug_error() { let msg = error_string(code); let kind = decode_error_kind(code); let err = Error { - repr: Repr::Custom(box Custom { + repr: Repr::new_custom(box Custom { kind: ErrorKind::InvalidInput, - error: box Error { repr: super::Repr::Os(code) }, + error: box Error { repr: super::Repr::new_os(code) }, }), }; let expected = format!( @@ -60,10 +61,83 @@ fn test_downcasting() { #[test] fn test_const() { - const E: Error = Error::new_const(ErrorKind::NotFound, &"hello"); + const E: Error = const_io_error!(ErrorKind::NotFound, "hello"); assert_eq!(E.kind(), ErrorKind::NotFound); assert_eq!(E.to_string(), "hello"); assert!(format!("{:?}", E).contains("\"hello\"")); assert!(format!("{:?}", E).contains("NotFound")); } + +#[test] +fn test_os_packing() { + for code in -20i32..20i32 { + let e = Error::from_raw_os_error(code); + assert_eq!(e.raw_os_error(), Some(code)); + assert_matches!( + e.repr.data(), + ErrorData::Os(c) if c == code, + ); + } +} + +#[test] +fn test_errorkind_packing() { + assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); + assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); + assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); + // Check that the innards look like like what we want. + assert_matches!( + Error::from(ErrorKind::OutOfMemory).repr.data(), + ErrorData::Simple(ErrorKind::OutOfMemory), + ); +} + +#[test] +fn test_simple_message_packing() { + use super::{ErrorKind::*, SimpleMessage}; + macro_rules! check_simple_msg { + ($err:expr, $kind:ident, $msg:literal) => {{ + let e = &$err; + // Check that the public api is right. + assert_eq!(e.kind(), $kind); + assert!(format!("{:?}", e).contains($msg)); + // and we got what we expected + assert_matches!( + e.repr.data(), + ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg }) + ); + }}; + } + + let not_static = const_io_error!(Uncategorized, "not a constant!"); + check_simple_msg!(not_static, Uncategorized, "not a constant!"); + + const CONST: Error = const_io_error!(NotFound, "definitely a constant!"); + check_simple_msg!(CONST, NotFound, "definitely a constant!"); + + static STATIC: Error = const_io_error!(BrokenPipe, "a constant, sort of!"); + check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!"); +} + +#[derive(Debug, PartialEq)] +struct Bojji(bool); +impl error::Error for Bojji {} +impl fmt::Display for Bojji { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ah! {:?}", self) + } +} + +#[test] +fn test_custom_error_packing() { + use super::Custom; + let test = Error::new(ErrorKind::Uncategorized, Bojji(true)); + assert_matches!( + test.repr.data(), + ErrorData::Custom(Custom { + kind: ErrorKind::Uncategorized, + error, + }) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), + ); +} diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 23201f9fc5..64d2457bce 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -5,7 +5,7 @@ use crate::alloc::Allocator; use crate::cmp; use crate::fmt; use crate::io::{ - self, BufRead, Error, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write, + self, BufRead, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write, }; use crate::mem; @@ -279,7 +279,10 @@ impl Read for &[u8] { #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if buf.len() > self.len() { - return Err(Error::new_const(ErrorKind::UnexpectedEof, &"failed to fill whole buffer")); + return Err(io::const_io_error!( + ErrorKind::UnexpectedEof, + "failed to fill whole buffer" + )); } let (a, b) = self.split_at(buf.len()); @@ -361,7 +364,7 @@ impl Write for &mut [u8] { if self.write(data)? == data.len() { Ok(()) } else { - Err(Error::new_const(ErrorKind::WriteZero, &"failed to write whole buffer")) + Err(io::const_io_error!(ErrorKind::WriteZero, "failed to write whole buffer")) } } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index ecc9e91b6b..71a59fb580 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -261,34 +261,28 @@ use crate::str; use crate::sys; use crate::sys_common::memchr; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::buffered::IntoInnerError; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::buffered::{BufReader, BufWriter, LineWriter}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::copy::copy; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::cursor::Cursor; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::error::{Error, ErrorKind, Result}; #[unstable(feature = "internal_output_capture", issue = "none")] #[doc(no_inline, hidden)] pub use self::stdio::set_output_capture; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; -#[unstable(feature = "stdio_locked", issue = "86845")] -pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; #[unstable(feature = "print_internals", issue = "none")] pub use self::stdio::{_eprint, _print}; +#[unstable(feature = "stdio_locked", issue = "86845")] +pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::util::{empty, repeat, sink, Empty, Repeat, Sink}; +pub use self::{ + buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, + copy::copy, + cursor::Cursor, + error::{Error, ErrorKind, Result}, + stdio::{stderr, stdin, stdout, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock}, + util::{empty, repeat, sink, Empty, Repeat, Sink}, +}; #[unstable(feature = "read_buf", issue = "78485")] pub use self::readbuf::ReadBuf; +pub(crate) use error::const_io_error; mod buffered; pub(crate) mod copy; @@ -344,7 +338,10 @@ where let ret = f(g.buf); if str::from_utf8(&g.buf[g.len..]).is_err() { ret.and_then(|_| { - Err(Error::new_const(ErrorKind::InvalidData, &"stream did not contain valid UTF-8")) + Err(error::const_io_error!( + ErrorKind::InvalidData, + "stream did not contain valid UTF-8" + )) }) } else { g.len = g.buf.len(); @@ -461,7 +458,7 @@ pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [ } } if !buf.is_empty() { - Err(Error::new_const(ErrorKind::UnexpectedEof, &"failed to fill whole buffer")) + Err(error::const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) } else { Ok(()) } @@ -1038,14 +1035,14 @@ pub trait Read { /// /// # use std::io; /// fn main() -> io::Result<()> { -/// let stdin = io::read_to_string(&mut io::stdin())?; +/// let stdin = io::read_to_string(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 { +pub fn read_to_string(mut reader: R) -> Result { let mut buf = String::new(); reader.read_to_string(&mut buf)?; Ok(buf) @@ -1519,9 +1516,9 @@ pub trait Write { while !buf.is_empty() { match self.write(buf) { Ok(0) => { - return Err(Error::new_const( + return Err(error::const_io_error!( ErrorKind::WriteZero, - &"failed to write whole buffer", + "failed to write whole buffer", )); } Ok(n) => buf = &buf[n..], @@ -1587,9 +1584,9 @@ pub trait Write { while !bufs.is_empty() { match self.write_vectored(bufs) { Ok(0) => { - return Err(Error::new_const( + return Err(error::const_io_error!( ErrorKind::WriteZero, - &"failed to write whole buffer", + "failed to write whole buffer", )); } Ok(n) => IoSlice::advance_slices(&mut bufs, n), @@ -1664,7 +1661,7 @@ pub trait Write { if output.error.is_err() { output.error } else { - Err(Error::new_const(ErrorKind::Uncategorized, &"formatter error")) + Err(error::const_io_error!(ErrorKind::Uncategorized, "formatter error")) } } } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index c072f0cafe..3d6de20d86 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -7,7 +7,7 @@ use crate::io::prelude::*; use crate::cell::{Cell, RefCell}; use crate::fmt; -use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, Split}; +use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines}; use crate::lazy::SyncOnceCell; use crate::pin::Pin; use crate::sync::atomic::{AtomicBool, Ordering}; @@ -465,29 +465,6 @@ impl Stdin { pub fn lines(self) -> Lines> { self.into_locked().lines() } - - /// Consumes this handle and returns an iterator over input bytes, - /// split at the specified byte value. - /// - /// For detailed semantics of this method, see the documentation on - /// [`BufRead::split`]. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(stdin_forwarders)] - /// use std::io; - /// - /// let splits = io::stdin().split(b'-'); - /// for split in splits { - /// println!("got a chunk: {}", String::from_utf8_lossy(&split.unwrap())); - /// } - /// ``` - #[must_use = "`self` will be dropped if the result is not used"] - #[unstable(feature = "stdin_forwarders", issue = "87096")] - pub fn split(self, byte: u8) -> Split> { - self.into_locked().split(byte) - } } #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index ea49bfe342..eb62634856 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -185,12 +185,12 @@ fn take_eof() { impl Read for R { fn read(&mut self, _: &mut [u8]) -> io::Result { - Err(io::Error::new_const(io::ErrorKind::Other, &"")) + Err(io::const_io_error!(io::ErrorKind::Other, "")) } } impl BufRead for R { fn fill_buf(&mut self) -> io::Result<&[u8]> { - Err(io::Error::new_const(io::ErrorKind::Other, &"")) + Err(io::const_io_error!(io::ErrorKind::Other, "")) } fn consume(&mut self, _amt: usize) {} } diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index a370485102..35d230eee9 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -2172,7 +2172,7 @@ mod use_keyword {} /// i.next().unwrap_or_else(I::Item::default) /// } /// -/// assert_eq!(first_or_default(vec![1, 2, 3].into_iter()), 1); +/// assert_eq!(first_or_default([1, 2, 3].into_iter()), 1); /// assert_eq!(first_or_default(Vec::::new().into_iter()), 0); /// ``` /// diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b002104897..8c38db9b62 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -35,8 +35,8 @@ //! development you may want to press the `[-]` button near the top of the //! page to collapse it into a more skimmable view. //! -//! While you are looking at that `[-]` button also notice the `[src]` -//! button. Rust's API documentation comes with the source code and you are +//! While you are looking at that `[-]` button also notice the `source` +//! link. Rust's API documentation comes with the source code and you are //! encouraged to read it. The standard library source is generally high //! quality and a peek behind the curtains is often enlightening. //! @@ -195,15 +195,12 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] -#![cfg_attr( - not(bootstrap), - doc(cfg_hide( - not(test), - not(any(test, bootstrap)), - no_global_oom_handling, - not(no_global_oom_handling) - )) -)] +#![doc(cfg_hide( + not(test), + not(any(test, bootstrap)), + no_global_oom_handling, + not(no_global_oom_handling) +))] // Don't link to std. We are std. #![no_std] #![warn(deprecated_in_future)] @@ -225,6 +222,7 @@ // std is implemented with unstable features, many of which are internal // compiler details that will never be stable // NB: the following list is sorted to minimize merge conflicts. +#![feature(absolute_path)] #![feature(alloc_error_handler)] #![feature(alloc_layout_extra)] #![feature(allocator_api)] @@ -235,7 +233,7 @@ #![feature(array_error_internals)] #![feature(assert_matches)] #![feature(associated_type_bounds)] -#![feature(async_stream)] +#![feature(async_iterator)] #![feature(atomic_mut_ptr)] #![feature(auto_traits)] #![feature(bench_black_box)] @@ -245,11 +243,11 @@ #![feature(c_variadic)] #![feature(cfg_accessible)] #![feature(cfg_eval)] -#![feature(cfg_target_has_atomic)] +#![cfg_attr(bootstrap, feature(cfg_target_has_atomic))] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] #![feature(char_internals)] -#![cfg_attr(not(bootstrap), feature(concat_bytes))] +#![feature(concat_bytes)] #![feature(concat_idents)] #![feature(const_fn_floating_point_arithmetic)] #![feature(const_fn_fn_ptr_basics)] @@ -294,10 +292,8 @@ #![feature(intra_doc_pointers)] #![feature(lang_items)] #![feature(linkage)] -#![feature(llvm_asm)] #![feature(log_syntax)] #![feature(map_try_insert)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] @@ -313,8 +309,10 @@ #![feature(once_cell)] #![feature(panic_info_message)] #![feature(panic_internals)] +#![feature(panic_can_unwind)] #![feature(panic_unwind)] #![feature(pin_static_ref)] +#![feature(platform_intrinsics)] #![feature(portable_simd)] #![feature(prelude_import)] #![feature(ptr_as_uninit)] @@ -342,7 +340,6 @@ #![feature(unboxed_closures)] #![feature(unwrap_infallible)] #![feature(vec_into_raw_parts)] -#![feature(vec_spare_capacity)] // NB: the above list is sorted to minimize merge conflicts. #![default_lib_allocator] @@ -405,15 +402,10 @@ pub use alloc_crate::string; pub use alloc_crate::vec; #[stable(feature = "rust1", since = "1.0.0")] pub use core::any; -#[stable(feature = "simd_arch", since = "1.27.0")] -// The `no_inline`-attribute is required to make the documentation of all -// targets available. -// See https://github.com/rust-lang/rust/pull/57808#issuecomment-457390549 for -// more information. -#[doc(no_inline)] // Note (#82861): required for correct documentation -pub use core::arch; #[stable(feature = "core_array", since = "1.36.0")] pub use core::array; +#[unstable(feature = "async_iterator", issue = "79024")] +pub use core::async_iter; #[stable(feature = "rust1", since = "1.0.0")] pub use core::cell; #[stable(feature = "rust1", since = "1.0.0")] @@ -468,10 +460,6 @@ pub use core::pin; pub use core::ptr; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; -#[unstable(feature = "portable_simd", issue = "86656")] -pub use core::simd; -#[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; @@ -516,6 +504,25 @@ pub mod time; #[unstable(feature = "once_cell", issue = "74465")] pub mod lazy; +// Pull in `std_float` crate into libstd. The contents of +// `std_float` are in a different repository: rust-lang/portable-simd. +#[path = "../../portable-simd/crates/std_float/src/lib.rs"] +#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn, unused_unsafe)] +#[allow(rustdoc::bare_urls)] +#[unstable(feature = "portable_simd", issue = "86656")] +#[cfg(not(all(miri, doctest)))] // Miri does not support all SIMD intrinsics +mod std_float; + +#[cfg(not(all(miri, doctest)))] // Miri does not support all SIMD intrinsics +#[doc = include_str!("../../portable-simd/crates/core_simd/src/core_simd_docs.md")] +#[unstable(feature = "portable_simd", issue = "86656")] +pub mod simd { + #[doc(inline)] + pub use crate::std_float::StdFloat; + #[doc(inline)] + pub use core::simd::*; +} + #[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. @@ -529,6 +536,32 @@ pub mod task { pub use alloc::task::*; } +#[doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")] +#[stable(feature = "simd_arch", since = "1.27.0")] +pub mod arch { + #[stable(feature = "simd_arch", since = "1.27.0")] + // The `no_inline`-attribute is required to make the documentation of all + // targets available. + // See https://github.com/rust-lang/rust/pull/57808#issuecomment-457390549 for + // more information. + #[doc(no_inline)] // Note (#82861): required for correct documentation + pub use core::arch::*; + + #[stable(feature = "simd_aarch64", since = "1.60.0")] + pub use std_detect::is_aarch64_feature_detected; + #[stable(feature = "simd_x86", since = "1.27.0")] + pub use std_detect::is_x86_feature_detected; + #[unstable(feature = "stdsimd", issue = "48556")] + pub use std_detect::{ + is_arm_feature_detected, is_mips64_feature_detected, is_mips_feature_detected, + is_powerpc64_feature_detected, is_powerpc_feature_detected, is_riscv_feature_detected, + }; +} + +// This was stabilized in the crate root so we have to keep it there. +#[stable(feature = "simd_x86", since = "1.27.0")] +pub use std_detect::is_x86_feature_detected; + // The runtime entry point and a few unstable public functions used by the // compiler #[macro_use] @@ -547,18 +580,6 @@ mod panicking; #[allow(dead_code, unused_attributes)] mod backtrace_rs; -#[stable(feature = "simd_x86", since = "1.27.0")] -pub use std_detect::is_x86_feature_detected; -#[doc(hidden)] -#[unstable(feature = "stdsimd", issue = "48556")] -pub use std_detect::*; -#[unstable(feature = "stdsimd", issue = "48556")] -pub use std_detect::{ - is_aarch64_feature_detected, is_arm_feature_detected, is_mips64_feature_detected, - is_mips_feature_detected, is_powerpc64_feature_detected, is_powerpc_feature_detected, - is_riscv_feature_detected, -}; - // Re-export macros defined in libcore. #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] @@ -572,8 +593,8 @@ pub use core::{ #[allow(deprecated)] pub use core::{ assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args, - env, file, format_args, format_args_nl, include, include_bytes, include_str, line, llvm_asm, - log_syntax, module_path, option_env, stringify, trace_macros, + env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax, + module_path, option_env, stringify, trace_macros, }; #[unstable( @@ -581,7 +602,6 @@ pub use core::{ issue = "87555", reason = "`concat_bytes` is not stable enough for use and is subject to change" )] -#[cfg(not(bootstrap))] pub use core::concat_bytes; #[stable(feature = "core_primitive", since = "1.43.0")] @@ -610,7 +630,3 @@ mod sealed { #[unstable(feature = "sealed", issue = "none")] pub trait Sealed {} } - -#[unstable(feature = "thread_local_const_init", issue = "91543")] -#[allow(unused)] -fn workaround_for_91543_as_racer_needs_this_feature_gate() {} diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 5dc75d32ec..23cbfaeef4 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -57,6 +57,7 @@ macro_rules! panic { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "print_macro")] #[allow_internal_unstable(print_internals)] macro_rules! print { ($($arg:tt)*) => ($crate::io::_print($crate::format_args!($($arg)*))); @@ -90,6 +91,7 @@ macro_rules! print { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "println_macro")] #[allow_internal_unstable(print_internals, format_args_nl)] macro_rules! println { () => ($crate::print!("\n")); @@ -121,6 +123,7 @@ macro_rules! println { /// ``` #[macro_export] #[stable(feature = "eprint", since = "1.19.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "eprint_macro")] #[allow_internal_unstable(print_internals)] macro_rules! eprint { ($($arg:tt)*) => ($crate::io::_eprint($crate::format_args!($($arg)*))); @@ -149,6 +152,7 @@ macro_rules! eprint { /// ``` #[macro_export] #[stable(feature = "eprint", since = "1.19.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "eprintln_macro")] #[allow_internal_unstable(print_internals, format_args_nl)] macro_rules! eprintln { () => ($crate::eprint!("\n")); @@ -282,6 +286,7 @@ macro_rules! eprintln { /// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html /// [`log`]: https://crates.io/crates/log #[macro_export] +#[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")] #[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 diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index a0c77b648f..f676e0a04f 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -17,7 +17,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::io::{self, Error, ErrorKind}; +use crate::io::{self, ErrorKind}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; @@ -25,6 +25,8 @@ pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::parser::AddrParseError; +#[unstable(feature = "tcplistener_into_incoming", issue = "88339")] +pub use self::tcp::IntoIncoming; #[stable(feature = "rust1", since = "1.0.0")] pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] @@ -88,6 +90,6 @@ where } } Err(last_err.unwrap_or_else(|| { - Error::new_const(ErrorKind::InvalidInput, &"could not resolve to any addresses") + io::const_io_error!(ErrorKind::InvalidInput, "could not resolve to any addresses") })) } diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 1ba54d892e..cc4e4fd4fd 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -405,7 +405,7 @@ impl TcpStream { /// use std::net::TcpStream; /// /// let stream = TcpStream::connect("127.0.0.1:8000") - /// .expect("couldn't bind to address"); + /// .expect("Couldn't connect to the server..."); /// let mut buf = [0; 10]; /// let len = stream.peek(&mut buf).expect("peek failed"); /// ``` diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 6354752e64..11a696e92c 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -2,7 +2,7 @@ mod tests; use crate::fmt; -use crate::io::{self, Error, ErrorKind}; +use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; use crate::sys_common::net as net_imp; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -175,7 +175,9 @@ impl UdpSocket { pub fn send_to(&self, buf: &[u8], addr: A) -> io::Result { match addr.to_socket_addrs()?.next() { Some(addr) => self.0.send_to(buf, &addr), - None => Err(Error::new_const(ErrorKind::InvalidInput, &"no addresses to send data to")), + None => { + Err(io::const_io_error!(ErrorKind::InvalidInput, "no addresses to send data to")) + } } } diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 52d7d4690d..71c660e718 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -8,6 +8,8 @@ use crate::fmt; use crate::fs; use crate::marker::PhantomData; use crate::mem::forget; +#[cfg(not(target_os = "wasi"))] +use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; /// A borrowed file descriptor. @@ -67,6 +69,37 @@ impl BorrowedFd<'_> { } } +impl OwnedFd { + /// Creates a new `OwnedFd` instance that shares the same underlying file handle + /// as the existing `OwnedFd` instance. + #[cfg(not(target_os = "wasi"))] + pub fn try_clone(&self) -> crate::io::Result { + // We want to atomically duplicate this file descriptor and set the + // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This + // is a POSIX flag that was added to Linux in 2.6.24. + #[cfg(not(target_os = "espidf"))] + let cmd = libc::F_DUPFD_CLOEXEC; + + // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics + // will never be supported, as this is a bare metal framework with + // no capabilities for multi-process execution. While F_DUPFD is also + // not supported yet, it might be (currently it returns ENOSYS). + #[cfg(target_os = "espidf")] + let cmd = libc::F_DUPFD; + + let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } + + #[cfg(target_os = "wasi")] + pub fn try_clone(&self) -> crate::io::Result { + Err(crate::io::const_io_error!( + crate::io::ErrorKind::Unsupported, + "operation not supported on WASI yet", + )) + } +} + #[unstable(feature = "io_safety", issue = "87074")] impl AsRawFd for BorrowedFd<'_> { #[inline] @@ -167,6 +200,22 @@ pub trait AsFd { fn as_fd(&self) -> BorrowedFd<'_>; } +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for &T { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + T::as_fd(self) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for &mut T { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + T::as_fd(self) + } +} + #[unstable(feature = "io_safety", issue = "87074")] impl AsFd for BorrowedFd<'_> { #[inline] diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index cd92dcabdf..d78049bce2 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -239,6 +239,7 @@ mod arch { target_arch = "riscv32" ))] mod arch { + #[stable(feature = "raw_ext", since = "1.1.0")] pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; } diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs index f0b38d2984..b6d5199341 100644 --- a/library/std/src/os/raw/mod.rs +++ b/library/std/src/os/raw/mod.rs @@ -45,94 +45,13 @@ macro_rules! type_alias { } } -type_alias! { "char.md", c_char = u8, NonZero_c_char = NonZeroU8; -#[doc(cfg(all()))] -#[cfg(any( - all( - target_os = "linux", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "hexagon", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "riscv64", - target_arch = "riscv32" - ) - ), - all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), - all(target_os = "l4re", target_arch = "x86_64"), - all( - target_os = "freebsd", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "riscv64" - ) - ), - all( - target_os = "netbsd", - any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") - ), - all(target_os = "openbsd", target_arch = "aarch64"), - all( - target_os = "vxworks", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc64", - target_arch = "powerpc" - ) - ), - all(target_os = "fuchsia", target_arch = "aarch64") -))]} -type_alias! { "char.md", c_char = i8, NonZero_c_char = NonZeroI8; -#[doc(cfg(all()))] -#[cfg(not(any( - all( - target_os = "linux", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "hexagon", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "riscv64", - target_arch = "riscv32" - ) - ), - all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), - all(target_os = "l4re", target_arch = "x86_64"), - all( - target_os = "freebsd", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "riscv64" - ) - ), - all( - target_os = "netbsd", - any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") - ), - all(target_os = "openbsd", target_arch = "aarch64"), - all( - target_os = "vxworks", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc64", - target_arch = "powerpc" - ) - ), - all(target_os = "fuchsia", target_arch = "aarch64") -)))]} +type_alias! { "char.md", c_char = c_char_definition::c_char, NonZero_c_char = c_char_definition::NonZero_c_char; +// Make this type alias appear cfg-dependent so that Clippy does not suggest +// replacing `0 as c_char` with `0_i8`/`0_u8`. This #[cfg(all())] can be removed +// after the false positive in https://github.com/rust-lang/rust-clippy/issues/8093 +// is fixed. +#[cfg(all())] +#[doc(cfg(all()))] } type_alias! { "schar.md", c_schar = i8, NonZero_c_schar = NonZeroI8; } type_alias! { "uchar.md", c_uchar = u8, NonZero_c_uchar = NonZeroU8; } type_alias! { "short.md", c_short = i16, NonZero_c_short = NonZeroI16; } @@ -180,3 +99,58 @@ pub type c_ptrdiff_t = isize; /// platforms where this is not the case. #[unstable(feature = "c_size_t", issue = "88345")] pub type c_ssize_t = isize; + +mod c_char_definition { + cfg_if::cfg_if! { + // These are the targets on which c_char is unsigned. + if #[cfg(any( + all( + target_os = "linux", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "hexagon", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "riscv64", + target_arch = "riscv32" + ) + ), + all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), + all(target_os = "l4re", target_arch = "x86_64"), + all( + target_os = "freebsd", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "riscv64" + ) + ), + all( + target_os = "netbsd", + any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") + ), + all(target_os = "openbsd", target_arch = "aarch64"), + all( + target_os = "vxworks", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc64", + target_arch = "powerpc" + ) + ), + all(target_os = "fuchsia", target_arch = "aarch64") + ))] { + pub type c_char = u8; + pub type NonZero_c_char = core::num::NonZeroU8; + } else { + // On every other target, c_char is signed. + pub type c_char = i8; + pub type NonZero_c_char = core::num::NonZeroI8; + } + } +} diff --git a/library/std/src/os/unix/ffi/os_str.rs b/library/std/src/os/unix/ffi/os_str.rs index 54c9a9382f..650f712bc6 100644 --- a/library/std/src/os/unix/ffi/os_str.rs +++ b/library/std/src/os/unix/ffi/os_str.rs @@ -28,9 +28,11 @@ pub trait OsStringExt: Sealed { #[stable(feature = "rust1", since = "1.0.0")] impl OsStringExt for OsString { + #[inline] fn from_vec(vec: Vec) -> OsString { FromInner::from_inner(Buf { inner: vec }) } + #[inline] fn into_vec(self) -> Vec { self.into_inner().inner } diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 0284a428b5..75d65e6d5f 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -114,7 +114,7 @@ pub trait FileExt { } } if !buf.is_empty() { - Err(io::Error::new_const(io::ErrorKind::UnexpectedEof, &"failed to fill whole buffer")) + Err(io::const_io_error!(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer",)) } else { Ok(()) } @@ -196,9 +196,9 @@ pub trait FileExt { while !buf.is_empty() { match self.write_at(buf, offset) { Ok(0) => { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::WriteZero, - &"failed to write whole buffer", + "failed to write whole buffer", )); } Ok(n) => { @@ -966,7 +966,7 @@ pub fn chown>(dir: P, uid: Option, gid: Option) -> io:: /// /// fn main() -> std::io::Result<()> { /// let f = std::fs::File::open("/file")?; -/// fs::fchown(f, Some(0), Some(0))?; +/// fs::fchown(&f, Some(0), Some(0))?; /// Ok(()) /// } /// ``` diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs index 6317e31747..a4d2ba797d 100644 --- a/library/std/src/os/unix/io/raw.rs +++ b/library/std/src/os/unix/io/raw.rs @@ -2,4 +2,5 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "rust1", since = "1.0.0")] pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index f450e41bfe..034fa301ba 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -2,7 +2,7 @@ use crate::ffi::OsStr; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; use crate::sys::cvt; -use crate::{ascii, fmt, io, iter, mem}; +use crate::{ascii, fmt, io, mem, ptr}; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? #[cfg(not(unix))] @@ -22,30 +22,33 @@ fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { path - base } -pub(super) unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { - let mut addr: libc::sockaddr_un = mem::zeroed(); +pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { + // SAFETY: All zeros is a valid representation for `sockaddr_un`. + let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() }; addr.sun_family = libc::AF_UNIX as libc::sa_family_t; let bytes = path.as_os_str().as_bytes(); if bytes.contains(&0) { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"paths must not contain interior null bytes", + "paths must not contain interior null bytes", )); } if bytes.len() >= addr.sun_path.len() { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"path must be shorter than SUN_LEN", + "path must be shorter than SUN_LEN", )); } - for (dst, src) in iter::zip(&mut addr.sun_path, bytes) { - *dst = *src as libc::c_char; - } - // null byte for pathname addresses is already there because we zeroed the - // struct + // SAFETY: `bytes` and `addr.sun_path` are not overlapping and + // both point to valid memory. + // NOTE: We zeroed the memory above, so the path is already null + // terminated. + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) + }; let mut len = sun_path_offset(&addr) + bytes.len(); match bytes.get(0) { @@ -118,15 +121,52 @@ impl SocketAddr { // linux returns zero bytes of address len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"file descriptor did not correspond to a Unix socket", + "file descriptor did not correspond to a Unix socket", )); } Ok(SocketAddr { addr, len }) } + /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. + /// + /// # Errors + /// + /// Returns an error if the path is longer than `SUN_LEN` or if it contains + /// NULL bytes. + /// + /// # Examples + /// + /// ``` + /// #![feature(unix_socket_creation)] + /// use std::os::unix::net::SocketAddr; + /// use std::path::Path; + /// + /// # fn main() -> std::io::Result<()> { + /// let address = SocketAddr::from_path("/path/to/socket")?; + /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); + /// # Ok(()) + /// # } + /// ``` + /// + /// Creating a `SocketAddr` with a NULL byte results in an error. + /// + /// ``` + /// #![feature(unix_socket_creation)] + /// use std::os::unix::net::SocketAddr; + /// + /// assert!(SocketAddr::from_path("/path/with/\0/bytes").is_err()); + /// ``` + #[unstable(feature = "unix_socket_creation", issue = "93423")] + pub fn from_path

(path: P) -> io::Result + where + P: AsRef, + { + sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len }) + } + /// Returns `true` if the address is unnamed. /// /// # Examples @@ -283,9 +323,9 @@ impl SocketAddr { addr.sun_family = libc::AF_UNIX as libc::sa_family_t; if namespace.len() + 1 > addr.sun_path.len() { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"namespace must be shorter than SUN_LEN", + "namespace must be shorter than SUN_LEN", )); } diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 5c62679f55..160c8f1eca 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -87,7 +87,7 @@ pub trait FileExt { } } if !buf.is_empty() { - Err(io::Error::new_const(io::ErrorKind::UnexpectedEof, &"failed to fill whole buffer")) + Err(io::const_io_error!(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) } else { Ok(()) } @@ -153,9 +153,9 @@ pub trait FileExt { while !buf.is_empty() { match self.write_at(buf, offset) { Ok(0) => { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::WriteZero, - &"failed to write whole buffer", + "failed to write whole buffer", )); } Ok(n) => { @@ -250,6 +250,21 @@ impl FileExt for fs::File { } fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { + let advice = match advice { + a if a == wasi::ADVICE_NORMAL.raw() => wasi::ADVICE_NORMAL, + a if a == wasi::ADVICE_SEQUENTIAL.raw() => wasi::ADVICE_SEQUENTIAL, + a if a == wasi::ADVICE_RANDOM.raw() => wasi::ADVICE_RANDOM, + a if a == wasi::ADVICE_WILLNEED.raw() => wasi::ADVICE_WILLNEED, + a if a == wasi::ADVICE_DONTNEED.raw() => wasi::ADVICE_DONTNEED, + a if a == wasi::ADVICE_NOREUSE.raw() => wasi::ADVICE_NOREUSE, + _ => { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "invalid parameter 'advice'", + )); + } + }; + self.as_inner().as_inner().advise(offset, len, advice) } @@ -539,5 +554,5 @@ pub fn symlink_path, U: AsRef>(old_path: P, new_path: U) -> fn osstr2str(f: &OsStr) -> io::Result<&str> { f.to_str() - .ok_or_else(|| io::Error::new_const(io::ErrorKind::Uncategorized, &"input must be utf-8")) + .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) } diff --git a/library/std/src/os/wasi/net/mod.rs b/library/std/src/os/wasi/net/mod.rs index e6bcf87887..73c097d4a5 100644 --- a/library/std/src/os/wasi/net/mod.rs +++ b/library/std/src/os/wasi/net/mod.rs @@ -1,3 +1,23 @@ //! WASI-specific networking functionality #![unstable(feature = "wasi_ext", issue = "71213")] + +use crate::io; +use crate::net; +use crate::sys_common::AsInner; + +/// WASI-specific extensions to [`std::net::TcpListener`]. +/// +/// [`std::net::TcpListener`]: crate::net::TcpListener +pub trait TcpListenerExt { + /// Accept a socket. + /// + /// This corresponds to the `sock_accept` syscall. + fn sock_accept(&self, flags: u16) -> io::Result; +} + +impl TcpListenerExt for net::TcpListener { + fn sock_accept(&self, flags: u16) -> io::Result { + self.as_inner().as_inner().as_inner().sock_accept(flags) + } +} diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 1527f5b6b0..8df6c54a41 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -6,9 +6,11 @@ use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::convert::TryFrom; use crate::fmt; use crate::fs; +use crate::io; use crate::marker::PhantomData; use crate::mem::forget; use crate::sys::c; +use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; /// A borrowed handle. @@ -144,6 +146,36 @@ impl TryFrom for OwnedHandle { } } +impl OwnedHandle { + /// Creates a new `OwnedHandle` instance that shares the same underlying file handle + /// as the existing `OwnedHandle` instance. + pub fn try_clone(&self) -> crate::io::Result { + self.duplicate(0, false, c::DUPLICATE_SAME_ACCESS) + } + + pub(crate) fn duplicate( + &self, + access: c::DWORD, + inherit: bool, + options: c::DWORD, + ) -> io::Result { + let mut ret = 0 as c::HANDLE; + cvt(unsafe { + let cur_proc = c::GetCurrentProcess(); + c::DuplicateHandle( + cur_proc, + self.as_raw_handle(), + cur_proc, + &mut ret, + access, + inherit as c::BOOL, + options, + ) + })?; + unsafe { Ok(Self::from_raw_handle(ret)) } + } +} + impl TryFrom for OwnedHandle { type Error = (); @@ -284,6 +316,22 @@ pub trait AsHandle { fn as_handle(&self) -> BorrowedHandle<'_>; } +#[unstable(feature = "io_safety", issue = "87074")] +impl AsHandle for &T { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + T::as_handle(self) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsHandle for &mut T { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + T::as_handle(self) + } +} + impl AsHandle for BorrowedHandle<'_> { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 23db66df09..2f13eb77a1 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -4,9 +4,13 @@ use super::raw::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; use crate::fmt; +use crate::io; use crate::marker::PhantomData; +use crate::mem; use crate::mem::forget; +use crate::sys; use crate::sys::c; +use crate::sys::cvt; /// A borrowed socket. /// @@ -69,6 +73,77 @@ impl BorrowedSocket<'_> { } } +impl OwnedSocket { + /// Creates a new `OwnedSocket` instance that shares the same underlying socket + /// as the existing `OwnedSocket` instance. + pub fn try_clone(&self) -> io::Result { + let mut info = unsafe { mem::zeroed::() }; + let result = unsafe { + c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info) + }; + sys::net::cvt(result)?; + let socket = unsafe { + c::WSASocketW( + info.iAddressFamily, + info.iSocketType, + info.iProtocol, + &mut info, + 0, + c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, + ) + }; + + if socket != c::INVALID_SOCKET { + unsafe { Ok(OwnedSocket::from_raw_socket(socket)) } + } else { + let error = unsafe { c::WSAGetLastError() }; + + if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL { + return Err(io::Error::from_raw_os_error(error)); + } + + let socket = unsafe { + c::WSASocketW( + info.iAddressFamily, + info.iSocketType, + info.iProtocol, + &mut info, + 0, + c::WSA_FLAG_OVERLAPPED, + ) + }; + + if socket == c::INVALID_SOCKET { + return Err(last_error()); + } + + unsafe { + let socket = OwnedSocket::from_raw_socket(socket); + socket.set_no_inherit()?; + Ok(socket) + } + } + } + + #[cfg(not(target_vendor = "uwp"))] + pub(crate) fn set_no_inherit(&self) -> io::Result<()> { + cvt(unsafe { + c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) + }) + .map(drop) + } + + #[cfg(target_vendor = "uwp")] + pub(crate) fn set_no_inherit(&self) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "Unavailable on UWP")) + } +} + +/// Returns the last error from the Windows socket interface. +fn last_error() -> io::Error { + io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) +} + impl AsRawSocket for BorrowedSocket<'_> { #[inline] fn as_raw_socket(&self) -> RawSocket { @@ -135,6 +210,22 @@ pub trait AsSocket { fn as_socket(&self) -> BorrowedSocket<'_>; } +#[unstable(feature = "io_safety", issue = "87074")] +impl AsSocket for &T { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + T::as_socket(self) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsSocket for &mut T { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + T::as_socket(self) + } +} + impl AsSocket for BorrowedSocket<'_> { #[inline] fn as_socket(&self) -> BorrowedSocket<'_> { diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index c0605b2f41..ac16f47614 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -5,6 +5,7 @@ use crate::any::Any; use crate::collections; use crate::panicking; +use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::{Mutex, RwLock}; use crate::thread::Result; @@ -36,6 +37,9 @@ pub use core::panic::panic_2021; #[stable(feature = "panic_hooks", since = "1.10.0")] pub use crate::panicking::{set_hook, take_hook}; +#[unstable(feature = "panic_update_hook", issue = "92649")] +pub use crate::panicking::update_hook; + #[stable(feature = "panic_hooks", since = "1.10.0")] pub use core::panic::{Location, PanicInfo}; @@ -199,5 +203,118 @@ pub fn always_abort() { crate::panicking::panic_count::set_always_abort(); } +/// The configuration for whether and how the default panic hook will capture +/// and display the backtrace. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[unstable(feature = "panic_backtrace_config", issue = "93346")] +#[non_exhaustive] +pub enum BacktraceStyle { + /// Prints a terser backtrace which ideally only contains relevant + /// information. + Short, + /// Prints a backtrace with all possible information. + Full, + /// Disable collecting and displaying backtraces. + Off, +} + +impl BacktraceStyle { + pub(crate) fn full() -> Option { + if cfg!(feature = "backtrace") { Some(BacktraceStyle::Full) } else { None } + } + + fn as_usize(self) -> usize { + match self { + BacktraceStyle::Short => 1, + BacktraceStyle::Full => 2, + BacktraceStyle::Off => 3, + } + } + + fn from_usize(s: usize) -> Option { + Some(match s { + 0 => return None, + 1 => BacktraceStyle::Short, + 2 => BacktraceStyle::Full, + 3 => BacktraceStyle::Off, + _ => unreachable!(), + }) + } +} + +// Tracks whether we should/can capture a backtrace, and how we should display +// that backtrace. +// +// Internally stores equivalent of an Option. +static SHOULD_CAPTURE: AtomicUsize = AtomicUsize::new(0); + +/// Configure whether the default panic hook will capture and display a +/// backtrace. +/// +/// The default value for this setting may be set by the `RUST_BACKTRACE` +/// environment variable; see the details in [`get_backtrace_style`]. +#[unstable(feature = "panic_backtrace_config", issue = "93346")] +pub fn set_backtrace_style(style: BacktraceStyle) { + if !cfg!(feature = "backtrace") { + // If the `backtrace` feature of this crate isn't enabled, skip setting. + return; + } + SHOULD_CAPTURE.store(style.as_usize(), Ordering::Release); +} + +/// Checks whether the standard library's panic hook will capture and print a +/// backtrace. +/// +/// This function will, if a backtrace style has not been set via +/// [`set_backtrace_style`], read the environment variable `RUST_BACKTRACE` to +/// determine a default value for the backtrace formatting: +/// +/// The first call to `get_backtrace_style` may read the `RUST_BACKTRACE` +/// environment variable if `set_backtrace_style` has not been called to +/// override the default value. After a call to `set_backtrace_style` or +/// `get_backtrace_style`, any changes to `RUST_BACKTRACE` will have no effect. +/// +/// `RUST_BACKTRACE` is read according to these rules: +/// +/// * `0` for `BacktraceStyle::Off` +/// * `full` for `BacktraceStyle::Full` +/// * `1` for `BacktraceStyle::Short` +/// * Other values are currently `BacktraceStyle::Short`, but this may change in +/// the future +/// +/// Returns `None` if backtraces aren't currently supported. +#[unstable(feature = "panic_backtrace_config", issue = "93346")] +pub fn get_backtrace_style() -> Option { + if !cfg!(feature = "backtrace") { + // If the `backtrace` feature of this crate isn't enabled quickly return + // `Unsupported` so this can be constant propagated all over the place + // to optimize away callers. + return None; + } + if let Some(style) = BacktraceStyle::from_usize(SHOULD_CAPTURE.load(Ordering::Acquire)) { + return Some(style); + } + + // Setting environment variables for Fuchsia components isn't a standard + // or easily supported workflow. For now, display backtraces by default. + let format = if cfg!(target_os = "fuchsia") { + BacktraceStyle::Full + } else { + crate::env::var_os("RUST_BACKTRACE") + .map(|x| { + if &x == "0" { + BacktraceStyle::Off + } else if &x == "full" { + BacktraceStyle::Full + } else { + BacktraceStyle::Short + } + }) + .unwrap_or(BacktraceStyle::Off) + }; + set_backtrace_style(format); + Some(format) +} + #[cfg(test)] mod tests; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 87854fe4f2..2b9ae3210d 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -9,6 +9,7 @@ #![deny(unsafe_op_in_unsafe_fn)] +use crate::panic::BacktraceStyle; use core::panic::{BoxMeUp, Location, PanicInfo}; use crate::any::Any; @@ -18,7 +19,7 @@ use crate::mem::{self, ManuallyDrop}; use crate::process; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sys::stdio::panic_output; -use crate::sys_common::backtrace::{self, RustBacktrace}; +use crate::sys_common::backtrace; use crate::sys_common::rwlock::StaticRWLock; use crate::sys_common::thread_info; use crate::thread; @@ -76,6 +77,12 @@ enum Hook { Custom(*mut (dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send)), } +impl Hook { + fn custom(f: impl Fn(&PanicInfo<'_>) + 'static + Sync + Send) -> Self { + Self::Custom(Box::into_raw(Box::new(f))) + } +} + static HOOK_LOCK: StaticRWLock = StaticRWLock::new(); static mut HOOK: Hook = Hook::Default; @@ -118,6 +125,11 @@ pub fn set_hook(hook: Box) + 'static + Sync + Send>) { panic!("cannot modify the panic hook from a panicking thread"); } + // SAFETY: + // + // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`. + // - The argument of `Box::from_raw` is always a valid pointer that was created using + // `Box::into_raw`. unsafe { let guard = HOOK_LOCK.write(); let old_hook = HOOK; @@ -167,6 +179,11 @@ pub fn take_hook() -> Box) + 'static + Sync + Send> { panic!("cannot modify the panic hook from a panicking thread"); } + // SAFETY: + // + // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`. + // - The argument of `Box::from_raw` is always a valid pointer that was created using + // `Box::into_raw`. unsafe { let guard = HOOK_LOCK.write(); let hook = HOOK; @@ -180,13 +197,76 @@ pub fn take_hook() -> Box) + 'static + Sync + Send> { } } +/// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with +/// a new panic handler that does something and then executes the old handler. +/// +/// [`take_hook`]: ./fn.take_hook.html +/// [`set_hook`]: ./fn.set_hook.html +/// +/// # Panics +/// +/// Panics if called from a panicking thread. +/// +/// # Examples +/// +/// The following will print the custom message, and then the normal output of panic. +/// +/// ```should_panic +/// #![feature(panic_update_hook)] +/// use std::panic; +/// +/// // Equivalent to +/// // let prev = panic::take_hook(); +/// // panic::set_hook(move |info| { +/// // println!("..."); +/// // prev(info); +/// // ); +/// panic::update_hook(move |prev, info| { +/// println!("Print custom message and execute panic handler as usual"); +/// prev(info); +/// }); +/// +/// panic!("Custom and then normal"); +/// ``` +#[unstable(feature = "panic_update_hook", issue = "92649")] +pub fn update_hook(hook_fn: F) +where + F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>) + + Sync + + Send + + 'static, +{ + if thread::panicking() { + panic!("cannot modify the panic hook from a panicking thread"); + } + + // SAFETY: + // + // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`. + // - The argument of `Box::from_raw` is always a valid pointer that was created using + // `Box::into_raw`. + unsafe { + let guard = HOOK_LOCK.write(); + let old_hook = HOOK; + HOOK = Hook::Default; + + let prev = match old_hook { + Hook::Default => Box::new(default_hook), + Hook::Custom(ptr) => Box::from_raw(ptr), + }; + + HOOK = Hook::custom(move |info| hook_fn(&prev, info)); + drop(guard); + } +} + fn default_hook(info: &PanicInfo<'_>) { // If this is a double panic, make sure that we print a backtrace // for this panic. Otherwise only print it if logging is enabled. - let backtrace_env = if panic_count::get_count() >= 2 { - RustBacktrace::Print(crate::backtrace_rs::PrintFmt::Full) + let backtrace = if panic_count::get_count() >= 2 { + BacktraceStyle::full() } else { - backtrace::rust_backtrace_env() + crate::panic::get_backtrace_style() }; // The current implementation always returns `Some`. @@ -207,10 +287,14 @@ fn default_hook(info: &PanicInfo<'_>) { static FIRST_PANIC: AtomicBool = AtomicBool::new(true); - match backtrace_env { - RustBacktrace::Print(format) => drop(backtrace::print(err, format)), - RustBacktrace::Disabled => {} - RustBacktrace::RuntimeDisabled => { + match backtrace { + Some(BacktraceStyle::Short) => { + drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Short)) + } + Some(BacktraceStyle::Full) => { + drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Full)) + } + Some(BacktraceStyle::Off) => { if FIRST_PANIC.swap(false, Ordering::SeqCst) { let _ = writeln!( err, @@ -218,6 +302,8 @@ fn default_hook(info: &PanicInfo<'_>) { ); } } + // If backtraces aren't supported, do nothing. + None => {} } }; @@ -497,9 +583,14 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { let msg = info.message().unwrap(); // The current implementation always returns Some crate::sys_common::backtrace::__rust_end_short_backtrace(move || { if let Some(msg) = msg.as_str() { - rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc); + rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc, info.can_unwind()); } else { - rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + rust_panic_with_hook( + &mut PanicPayload::new(msg), + info.message(), + loc, + info.can_unwind(), + ); } }) } @@ -523,7 +614,7 @@ pub const fn begin_panic(msg: M) -> ! { let loc = Location::caller(); return crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc) + rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc, true) }); struct PanicPayload { @@ -568,6 +659,7 @@ fn rust_panic_with_hook( payload: &mut dyn BoxMeUp, message: Option<&fmt::Arguments<'_>>, location: &Location<'_>, + can_unwind: bool, ) -> ! { let (must_abort, panics) = panic_count::increase(); @@ -584,14 +676,14 @@ fn rust_panic_with_hook( } else { // Unfortunately, this does not print a backtrace, because creating // a `Backtrace` will allocate, which we must to avoid here. - let panicinfo = PanicInfo::internal_constructor(message, location); + let panicinfo = PanicInfo::internal_constructor(message, location, can_unwind); rtprintpanic!("{}\npanicked after panic::always_abort(), aborting.\n", panicinfo); } - intrinsics::abort() + crate::sys::abort_internal(); } unsafe { - let mut info = PanicInfo::internal_constructor(message, location); + let mut info = PanicInfo::internal_constructor(message, location, can_unwind); let _guard = HOOK_LOCK.read(); match HOOK { // Some platforms (like wasm) know that printing to stderr won't ever actually @@ -612,13 +704,13 @@ fn rust_panic_with_hook( }; } - if panics > 1 { + if panics > 1 || !can_unwind { // If a thread panics while it's already unwinding then we // have limited options. Currently our preference is to // just abort. In the future we may consider resuming // unwinding or otherwise exiting the thread cleanly. rtprintpanic!("thread panicked while panicking. aborting.\n"); - intrinsics::abort() + crate::sys::abort_internal(); } rust_panic(payload) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index bfbcb009f8..e544608f83 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -72,6 +72,7 @@ mod tests; use crate::borrow::{Borrow, Cow}; use crate::cmp; +use crate::collections::TryReserveError; use crate::error::Error; use crate::fmt; use crate::fs; @@ -84,7 +85,7 @@ use crate::str::FromStr; use crate::sync::Arc; use crate::ffi::{OsStr, OsString}; - +use crate::sys; use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR}; //////////////////////////////////////////////////////////////////////////////// @@ -267,6 +268,12 @@ pub fn is_separator(c: char) -> bool { #[stable(feature = "rust1", since = "1.0.0")] pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP; +/// The primary separator of path components for the current platform. +/// +/// For example, `/` on Unix and `\` on Windows. +#[unstable(feature = "main_separator_str", issue = "94071")] +pub const MAIN_SEPARATOR_STR: &str = crate::sys::path::MAIN_SEP_STR; + //////////////////////////////////////////////////////////////////////////////// // Misc helpers //////////////////////////////////////////////////////////////////////////////// @@ -1512,6 +1519,15 @@ impl PathBuf { self.inner.reserve(additional) } + /// Invokes [`try_reserve`] on the underlying instance of [`OsString`]. + /// + /// [`try_reserve`]: OsString::try_reserve + #[unstable(feature = "try_reserve_2", issue = "91789")] + #[inline] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve(additional) + } + /// Invokes [`reserve_exact`] on the underlying instance of [`OsString`]. /// /// [`reserve_exact`]: OsString::reserve_exact @@ -1521,6 +1537,15 @@ impl PathBuf { self.inner.reserve_exact(additional) } + /// Invokes [`try_reserve_exact`] on the underlying instance of [`OsString`]. + /// + /// [`try_reserve_exact`]: OsString::try_reserve_exact + #[unstable(feature = "try_reserve_2", issue = "91789")] + #[inline] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve_exact(additional) + } + /// Invokes [`shrink_to_fit`] on the underlying instance of [`OsString`]. /// /// [`shrink_to_fit`]: OsString::shrink_to_fit @@ -1581,7 +1606,7 @@ impl From> for Box { #[stable(feature = "path_buf_from_box", since = "1.18.0")] impl From> for PathBuf { - /// Converts a `Box` into a `PathBuf` + /// Converts a [Box]<[Path]> into a [`PathBuf`]. /// /// This conversion does not allocate or copy memory. #[inline] @@ -1592,7 +1617,7 @@ impl From> for PathBuf { #[stable(feature = "box_from_path_buf", since = "1.20.0")] impl From for Box { - /// Converts a `PathBuf` into a `Box` + /// Converts a [`PathBuf`] into a [Box]<[Path]>. /// /// This conversion currently should not allocate memory, /// but this behavior is not guaranteed on all platforms or in all future versions. @@ -1612,7 +1637,7 @@ impl Clone for Box { #[stable(feature = "rust1", since = "1.0.0")] impl> From<&T> for PathBuf { - /// Converts a borrowed `OsStr` to a `PathBuf`. + /// Converts a borrowed [`OsStr`] to a [`PathBuf`]. /// /// Allocates a [`PathBuf`] and copies the data into it. #[inline] @@ -1766,7 +1791,8 @@ impl<'a> From> for PathBuf { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { - /// Converts a [`PathBuf`] into an [`Arc`] by moving the [`PathBuf`] data into a new [`Arc`] buffer. + /// Converts a [`PathBuf`] into an [Arc]<[Path]> by moving the [`PathBuf`] data + /// into a new [`Arc`] buffer. #[inline] fn from(s: PathBuf) -> Arc { let arc: Arc = Arc::from(s.into_os_string()); @@ -1786,7 +1812,8 @@ impl From<&Path> for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Rc { - /// Converts a [`PathBuf`] into an [`Rc`] by moving the [`PathBuf`] data into a new `Rc` buffer. + /// Converts a [`PathBuf`] into an [Rc]<[Path]> by moving the [`PathBuf`] data into + /// a new [`Rc`] buffer. #[inline] fn from(s: PathBuf) -> Rc { let rc: Rc = Rc::from(s.into_os_string()); @@ -1796,7 +1823,7 @@ impl From for Rc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&Path> for Rc { - /// Converts a [`Path`] into an [`Rc`] by copying the [`Path`] data into a new `Rc` buffer. + /// Converts a [`Path`] into an [`Rc`] by copying the [`Path`] data into a new [`Rc`] buffer. #[inline] fn from(s: &Path) -> Rc { let rc: Rc = Rc::from(s.as_os_str()); @@ -2709,7 +2736,7 @@ impl Path { /// This function will traverse symbolic links to query information about the /// destination file. In case of broken symbolic links this will return `Ok(false)`. /// - /// As opposed to the `exists()` method, this one doesn't silently ignore errors + /// As opposed to the [`exists()`] method, this one doesn't silently ignore errors /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission /// denied on some of the parent directories.) /// @@ -2722,6 +2749,8 @@ impl Path { /// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt")); /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err()); /// ``` + /// + /// [`exists()`]: Self::exists // FIXME: stabilization should modify documentation of `exists()` to recommend this method // instead. #[unstable(feature = "path_try_exists", issue = "83186")] @@ -2806,7 +2835,7 @@ impl Path { /// use std::os::unix::fs::symlink; /// /// let link_path = Path::new("link"); - /// symlink("/origin_does_not_exists/", link_path).unwrap(); + /// symlink("/origin_does_not_exist/", link_path).unwrap(); /// assert_eq!(link_path.is_symlink(), true); /// assert_eq!(link_path.exists(), false); /// ``` @@ -3149,3 +3178,79 @@ impl Error for StripPrefixError { "prefix not found" } } + +/// Makes the path absolute without accessing the filesystem. +/// +/// If the path is relative, the current directory is used as the base directory. +/// All intermediate components will be resolved according to platforms-specific +/// rules but unlike [`canonicalize`][crate::fs::canonicalize] this does not +/// resolve symlinks and may succeed even if the path does not exist. +/// +/// If the `path` is empty or getting the +/// [current directory][crate::env::current_dir] fails then an error will be +/// returned. +/// +/// # Examples +/// +/// ## Posix paths +/// +/// ``` +/// #![feature(absolute_path)] +/// # #[cfg(unix)] +/// fn main() -> std::io::Result<()> { +/// use std::path::{self, Path}; +/// +/// // Relative to absolute +/// let absolute = path::absolute("foo/./bar")?; +/// assert!(absolute.ends_with("foo/bar")); +/// +/// // Absolute to absolute +/// let absolute = path::absolute("/foo//test/.././bar.rs")?; +/// assert_eq!(absolute, Path::new("/foo/test/../bar.rs")); +/// Ok(()) +/// } +/// # #[cfg(not(unix))] +/// # fn main() {} +/// ``` +/// +/// The path is resolved using [POSIX semantics][posix-semantics] except that +/// it stops short of resolving symlinks. This means it will keep `..` +/// components and trailing slashes. +/// +/// ## Windows paths +/// +/// ``` +/// #![feature(absolute_path)] +/// # #[cfg(windows)] +/// fn main() -> std::io::Result<()> { +/// use std::path::{self, Path}; +/// +/// // Relative to absolute +/// let absolute = path::absolute("foo/./bar")?; +/// assert!(absolute.ends_with(r"foo\bar")); +/// +/// // Absolute to absolute +/// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?; +/// +/// assert_eq!(absolute, Path::new(r"C:\foo\bar.rs")); +/// Ok(()) +/// } +/// # #[cfg(not(windows))] +/// # fn main() {} +/// ``` +/// +/// For verbatim paths this will simply return the path as given. For other +/// paths this is currently equivalent to calling [`GetFullPathNameW`][windows-path] +/// This may change in the future. +/// +/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 +/// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew +#[unstable(feature = "absolute_path", issue = "92750")] +pub fn absolute>(path: P) -> io::Result { + let path = path.as_ref(); + if path.as_os_str().is_empty() { + Err(io::const_io_error!(io::ErrorKind::InvalidInput, "cannot make an empty path absolute",)) + } else { + sys::path::absolute(path) + } +} diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 0ab5956e1b..8e51433094 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1700,6 +1700,64 @@ fn test_ord() { ord!(Equal, "foo/bar", "foo/bar//"); } +#[test] +#[cfg(unix)] +fn test_unix_absolute() { + use crate::path::absolute; + + assert!(absolute("").is_err()); + + let relative = "a/b"; + let mut expected = crate::env::current_dir().unwrap(); + expected.push(relative); + assert_eq!(absolute(relative).unwrap(), expected); + + // Test how components are collected. + assert_eq!(absolute("/a/b/c").unwrap(), Path::new("/a/b/c")); + assert_eq!(absolute("/a//b/c").unwrap(), Path::new("/a/b/c")); + assert_eq!(absolute("//a/b/c").unwrap(), Path::new("//a/b/c")); + assert_eq!(absolute("///a/b/c").unwrap(), Path::new("/a/b/c")); + assert_eq!(absolute("/a/b/c/").unwrap(), Path::new("/a/b/c/")); + assert_eq!(absolute("/a/./b/../c/.././..").unwrap(), Path::new("/a/b/../c/../..")); +} + +#[test] +#[cfg(windows)] +fn test_windows_absolute() { + use crate::path::absolute; + // An empty path is an error. + assert!(absolute("").is_err()); + + let relative = r"a\b"; + let mut expected = crate::env::current_dir().unwrap(); + expected.push(relative); + assert_eq!(absolute(relative).unwrap(), expected); + + macro_rules! unchanged( + ($path:expr) => { + assert_eq!(absolute($path).unwrap(), Path::new($path)); + } + ); + + unchanged!(r"C:\path\to\file"); + unchanged!(r"C:\path\to\file\"); + unchanged!(r"\\server\share\to\file"); + unchanged!(r"\\server.\share.\to\file"); + unchanged!(r"\\.\PIPE\name"); + unchanged!(r"\\.\C:\path\to\COM1"); + unchanged!(r"\\?\C:\path\to\file"); + unchanged!(r"\\?\UNC\server\share\to\file"); + unchanged!(r"\\?\PIPE\name"); + // Verbatim paths are always unchanged, no matter what. + unchanged!(r"\\?\path.\to/file.."); + + assert_eq!(absolute(r"C:\path..\to.\file.").unwrap(), Path::new(r"C:\path..\to\file")); + assert_eq!(absolute(r"C:\path\to\COM1").unwrap(), Path::new(r"\\.\COM1")); + assert_eq!(absolute(r"C:\path\to\COM1.txt").unwrap(), Path::new(r"\\.\COM1")); + assert_eq!(absolute(r"C:\path\to\COM1 .txt").unwrap(), Path::new(r"\\.\COM1")); + assert_eq!(absolute(r"C:\path\to\cOnOuT$").unwrap(), Path::new(r"\\.\cOnOuT$")); +} + #[bench] fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { let prefix = "my/home"; diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index b52bcdfca9..0226c4d7a2 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -40,9 +40,8 @@ pub use crate::result::Result::{self, Err, Ok}; #[doc(no_inline)] pub use core::prelude::v1::{ assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, llvm_asm, log_syntax, module_path, - option_env, stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, - PartialOrd, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, }; #[unstable( @@ -50,7 +49,6 @@ pub use core::prelude::v1::{ issue = "87555", reason = "`concat_bytes` is not stable enough for use and is subject to change" )] -#[cfg(not(bootstrap))] #[doc(no_inline)] pub use core::prelude::v1::concat_bytes; diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 8fcd8cdeb1..ebb1d8971b 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -275,20 +275,69 @@ mod prim_bool {} mod prim_never {} #[doc(primitive = "char")] +#[allow(rustdoc::invalid_rust_codeblocks)] /// A character type. /// /// The `char` type represents a single character. More specifically, since /// 'character' isn't a well-defined concept in Unicode, `char` is a '[Unicode -/// scalar value]', which is similar to, but not the same as, a '[Unicode code -/// point]'. -/// -/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value -/// [Unicode code point]: https://www.unicode.org/glossary/#code_point +/// scalar value]'. /// /// This documentation describes a number of methods and trait implementations on the /// `char` type. For technical reasons, there is additional, separate /// documentation in [the `std::char` module](char/index.html) as well. /// +/// # Validity +/// +/// A `char` is a '[Unicode scalar value]', which is any '[Unicode code point]' +/// other than a [surrogate code point]. This has a fixed numerical definition: +/// code points are in the range 0 to 0x10FFFF, inclusive. +/// Surrogate code points, used by UTF-16, are in the range 0xD800 to 0xDFFF. +/// +/// No `char` may be constructed, whether as a literal or at runtime, that is not a +/// Unicode scalar value: +/// +/// ```compile_fail +/// // Each of these is a compiler error +/// ['\u{D800}', '\u{DFFF}', '\u{110000}']; +/// ``` +/// +/// ```should_panic +/// // Panics; from_u32 returns None. +/// char::from_u32(0xDE01).unwrap(); +/// ``` +/// +/// ```no_run +/// // Undefined behaviour +/// unsafe { char::from_u32_unchecked(0x110000) }; +/// ``` +/// +/// USVs are also the exact set of values that may be encoded in UTF-8. Because +/// `char` values are USVs and `str` values are valid UTF-8, it is safe to store +/// any `char` in a `str` or read any character from a `str` as a `char`. +/// +/// The gap in valid `char` values is understood by the compiler, so in the +/// below example the two ranges are understood to cover the whole range of +/// possible `char` values and there is no error for a [non-exhaustive match]. +/// +/// ``` +/// let c: char = 'a'; +/// match c { +/// '\0' ..= '\u{D7FF}' => false, +/// '\u{E000}' ..= '\u{10FFFF}' => true, +/// }; +/// ``` +/// +/// All USVs are valid `char` values, but not all of them represent a real +/// character. Many USVs are not currently assigned to a character, but may be +/// in the future ("reserved"); some will never be a character +/// ("noncharacters"); and some may be given different meanings by different +/// users ("private use"). +/// +/// [Unicode code point]: https://www.unicode.org/glossary/#code_point +/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value +/// [non-exhaustive match]: ../book/ch06-02-match.html#matches-are-exhaustive +/// [surrogate code point]: https://www.unicode.org/glossary/#surrogate_code_point +/// /// # Representation /// /// `char` is always four bytes in size. This is a different representation than diff --git a/library/std/src/process.rs b/library/std/src/process.rs index e012594dd4..e3fff155e4 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1277,7 +1277,7 @@ impl fmt::Debug for Stdio { #[stable(feature = "stdio_from", since = "1.20.0")] impl From for Stdio { - /// Converts a `ChildStdin` into a `Stdio` + /// Converts a [`ChildStdin`] into a [`Stdio`]. /// /// # Examples /// @@ -1306,7 +1306,7 @@ impl From for Stdio { #[stable(feature = "stdio_from", since = "1.20.0")] impl From for Stdio { - /// Converts a `ChildStdout` into a `Stdio` + /// Converts a [`ChildStdout`] into a [`Stdio`]. /// /// # Examples /// @@ -1335,7 +1335,7 @@ impl From for Stdio { #[stable(feature = "stdio_from", since = "1.20.0")] impl From for Stdio { - /// Converts a `ChildStderr` into a `Stdio` + /// Converts a [`ChildStderr`] into a [`Stdio`]. /// /// # Examples /// @@ -1366,7 +1366,7 @@ impl From for Stdio { #[stable(feature = "stdio_from", since = "1.20.0")] impl From for Stdio { - /// Converts a `File` into a `Stdio` + /// Converts a [`File`](fs::File) into a [`Stdio`]. /// /// # Examples /// @@ -1676,6 +1676,29 @@ impl ExitCode { pub const FAILURE: ExitCode = ExitCode(imp::ExitCode::FAILURE); } +impl ExitCode { + // This should not be stabilized when stabilizing ExitCode, we don't know that i32 will serve + // all usecases, for example windows seems to use u32, unix uses the 8-15th bits of an i32, we + // likely want to isolate users anything that could restrict the platform specific + // representation of an ExitCode + // + // More info: https://internals.rust-lang.org/t/mini-pre-rfc-redesigning-process-exitstatus/5426 + /// Convert an ExitCode into an i32 + #[unstable(feature = "process_exitcode_placeholder", issue = "48711")] + #[inline] + pub fn to_i32(self) -> i32 { + self.0.as_i32() + } +} + +#[unstable(feature = "process_exitcode_placeholder", issue = "48711")] +impl From for ExitCode { + /// Construct an exit code from an arbitrary u8 value. + fn from(code: u8) -> Self { + ExitCode(imp::ExitCode::from(code)) + } +} + impl Child { /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`] /// error is returned. @@ -2016,20 +2039,20 @@ pub fn id() -> u32 { pub trait Termination { /// Is called to get the representation of the value as status code. /// This status code is returned to the operating system. - fn report(self) -> i32; + fn report(self) -> ExitCode; } #[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for () { #[inline] - fn report(self) -> i32 { + fn report(self) -> ExitCode { ExitCode::SUCCESS.report() } } #[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for Result<(), E> { - fn report(self) -> i32 { + fn report(self) -> ExitCode { match self { Ok(()) => ().report(), Err(err) => Err::(err).report(), @@ -2039,14 +2062,14 @@ impl Termination for Result<(), E> { #[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for ! { - fn report(self) -> i32 { + fn report(self) -> ExitCode { self } } #[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for Result { - fn report(self) -> i32 { + fn report(self) -> ExitCode { let Err(err) = self; eprintln!("Error: {:?}", err); ExitCode::FAILURE.report() @@ -2055,7 +2078,7 @@ impl Termination for Result { #[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for Result { - fn report(self) -> i32 { + fn report(self) -> ExitCode { let Err(err) = self; Err::(err).report() } @@ -2064,7 +2087,7 @@ impl Termination for Result { #[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for ExitCode { #[inline] - fn report(self) -> i32 { - self.0.as_i32() + fn report(self) -> ExitCode { + self } } diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 08e5825757..663537a05f 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -142,7 +142,7 @@ fn lang_start( argv: *const *const u8, ) -> isize { let Ok(v) = lang_start_internal( - &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report(), + &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, ); diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 57f1dcca30..3ea0a6c393 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -188,12 +188,9 @@ unsafe impl Sync for Mutex {} /// [`lock`]: Mutex::lock /// [`try_lock`]: Mutex::try_lock #[must_use = "if unused the Mutex will immediately unlock"] -#[cfg_attr( - not(bootstrap), - must_not_suspend = "holding a MutexGuard across suspend \ +#[must_not_suspend = "holding a MutexGuard across suspend \ points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`" -)] + and cause Futures to not implement `Send`"] #[stable(feature = "rust1", since = "1.0.0")] pub struct MutexGuard<'a, T: ?Sized + 'a> { lock: &'a Mutex, diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index 2f4395ceef..2e72a9ef54 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -95,12 +95,9 @@ unsafe impl Sync for RwLock {} /// [`read`]: RwLock::read /// [`try_read`]: RwLock::try_read #[must_use = "if unused the RwLock will immediately unlock"] -#[cfg_attr( - not(bootstrap), - must_not_suspend = "holding a RwLockReadGuard across suspend \ +#[must_not_suspend = "holding a RwLockReadGuard across suspend \ points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`" -)] + and cause Futures to not implement `Send`"] #[stable(feature = "rust1", since = "1.0.0")] pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { lock: &'a RwLock, @@ -121,12 +118,9 @@ unsafe impl Sync for RwLockReadGuard<'_, T> {} /// [`write`]: RwLock::write /// [`try_write`]: RwLock::try_write #[must_use = "if unused the RwLock will immediately unlock"] -#[cfg_attr( - not(bootstrap), - must_not_suspend = "holding a RwLockWriteGuard across suspend \ +#[must_not_suspend = "holding a RwLockWriteGuard across suspend \ points can cause deadlocks, delays, \ - and cause Future's to not implement `Send`" -)] + and cause Future's to not implement `Send`"] #[stable(feature = "rust1", since = "1.0.0")] pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { lock: &'a RwLock, diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs index 9665d1fa89..e06eaf6db1 100644 --- a/library/std/src/sys/common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -14,8 +14,8 @@ use crate::ptr; target_arch = "asmjs", target_arch = "wasm32", target_arch = "hexagon", - target_arch = "riscv32", - target_arch = "xtensa" + all(target_arch = "riscv32", not(target_os = "espidf")), + all(target_arch = "xtensa", not(target_os = "espidf")), )))] pub const MIN_ALIGN: usize = 8; #[cfg(all(any( @@ -28,6 +28,12 @@ pub const MIN_ALIGN: usize = 8; target_arch = "wasm64", )))] pub const MIN_ALIGN: usize = 16; +// The allocator on the esp-idf platform guarentees 4 byte alignment. +#[cfg(all(any( + all(target_arch = "riscv32", target_os = "espidf"), + all(target_arch = "xtensa", target_os = "espidf"), +)))] +pub const MIN_ALIGN: usize = 4; pub unsafe fn realloc_fallback( alloc: &System, diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 974c44eb8d..fa9a7fb19e 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -226,7 +226,7 @@ impl OpenOptions { (false, _, true) => Ok(O_WRONLY | O_APPEND), (true, _, true) => Ok(O_RDWR | O_APPEND), (false, false, false) => { - Err(io::Error::new_const(ErrorKind::InvalidInput, &"invalid access mode")) + Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid access mode")) } } } @@ -236,17 +236,17 @@ impl OpenOptions { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(io::Error::new_const( + return Err(io::const_io_error!( ErrorKind::InvalidInput, - &"invalid creation mode", + "invalid creation mode", )); } } (_, true) => { if self.truncate && !self.create_new { - return Err(io::Error::new_const( + return Err(io::const_io_error!( ErrorKind::InvalidInput, - &"invalid creation mode", + "invalid creation mode", )); } } diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 185b68c0a7..b798c97448 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -58,9 +58,9 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::Error::new_const( + crate::io::const_io_error!( crate::io::ErrorKind::Unsupported, - &"operation not supported on HermitCore yet", + "operation not supported on HermitCore yet", ) } diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index 1a6b3bc63e..f65fd8e53b 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -14,9 +14,9 @@ use crate::time::Duration; /// if not, starts it. pub fn init() -> io::Result<()> { if abi::network_init() < 0 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( ErrorKind::Uncategorized, - &"Unable to initialize network interface", + "Unable to initialize network interface", )); } @@ -50,9 +50,9 @@ impl TcpStream { match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) { Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))), - _ => Err(io::Error::new_const( + _ => Err(io::const_io_error!( ErrorKind::Uncategorized, - &"Unable to initiate a connection on a socket", + "Unable to initiate a connection on a socket", )), } } @@ -64,9 +64,9 @@ impl TcpStream { Some(duration.as_millis() as u64), ) { Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))), - _ => Err(io::Error::new_const( + _ => Err(io::const_io_error!( ErrorKind::Uncategorized, - &"Unable to initiate a connection on a socket", + "Unable to initiate a connection on a socket", )), } } @@ -74,7 +74,7 @@ impl TcpStream { pub fn set_read_timeout(&self, duration: Option) -> io::Result<()> { abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64)) .map_err(|_| { - io::Error::new_const(ErrorKind::Uncategorized, &"Unable to set timeout value") + io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value") }) } @@ -83,12 +83,12 @@ impl TcpStream { *self.0.as_inner(), duration.map(|d| d.as_millis() as u64), ) - .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"Unable to set timeout value")) + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value")) } pub fn read_timeout(&self) -> io::Result> { let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| { - io::Error::new_const(ErrorKind::Uncategorized, &"Unable to determine timeout value") + io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value") })?; Ok(duration.map(|d| Duration::from_millis(d))) @@ -96,7 +96,7 @@ impl TcpStream { pub fn write_timeout(&self) -> io::Result> { let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| { - io::Error::new_const(ErrorKind::Uncategorized, &"Unable to determine timeout value") + io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value") })?; Ok(duration.map(|d| Duration::from_millis(d))) @@ -104,7 +104,7 @@ impl TcpStream { pub fn peek(&self, buf: &mut [u8]) -> io::Result { abi::tcpstream::peek(*self.0.as_inner(), buf) - .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"peek failed")) + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peek failed")) } pub fn read(&self, buffer: &mut [u8]) -> io::Result { @@ -116,7 +116,7 @@ impl TcpStream { for i in ioslice.iter_mut() { let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| { - io::Error::new_const(ErrorKind::Uncategorized, &"Unable to read on socket") + io::const_io_error!(ErrorKind::Uncategorized, "Unable to read on socket") })?; if ret != 0 { @@ -141,7 +141,7 @@ impl TcpStream { for i in ioslice.iter() { size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| { - io::Error::new_const(ErrorKind::Uncategorized, &"Unable to write on socket") + io::const_io_error!(ErrorKind::Uncategorized, "Unable to write on socket") })?; } @@ -155,13 +155,13 @@ impl TcpStream { pub fn peer_addr(&self) -> io::Result { let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner()) - .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"peer_addr failed"))?; + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"))?; let saddr = match ipaddr { Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port), Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port), _ => { - return Err(io::Error::new_const(ErrorKind::Uncategorized, &"peer_addr failed")); + return Err(io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed")); } }; @@ -173,9 +173,8 @@ impl TcpStream { } pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - abi::tcpstream::shutdown(*self.0.as_inner(), how as i32).map_err(|_| { - io::Error::new_const(ErrorKind::Uncategorized, &"unable to shutdown socket") - }) + abi::tcpstream::shutdown(*self.0.as_inner(), how as i32) + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to shutdown socket")) } pub fn duplicate(&self) -> io::Result { @@ -192,22 +191,22 @@ impl TcpStream { pub fn set_nodelay(&self, mode: bool) -> io::Result<()> { abi::tcpstream::set_nodelay(*self.0.as_inner(), mode) - .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"set_nodelay failed")) + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "set_nodelay failed")) } pub fn nodelay(&self) -> io::Result { abi::tcpstream::nodelay(*self.0.as_inner()) - .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"nodelay failed")) + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "nodelay failed")) } pub fn set_ttl(&self, tll: u32) -> io::Result<()> { abi::tcpstream::set_tll(*self.0.as_inner(), tll) - .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"unable to set TTL")) + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to set TTL")) } pub fn ttl(&self) -> io::Result { abi::tcpstream::get_tll(*self.0.as_inner()) - .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"unable to get TTL")) + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to get TTL")) } pub fn take_error(&self) -> io::Result> { @@ -216,7 +215,7 @@ impl TcpStream { pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> { abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode).map_err(|_| { - io::Error::new_const(ErrorKind::Uncategorized, &"unable to set blocking mode") + io::const_io_error!(ErrorKind::Uncategorized, "unable to set blocking mode") }) } } @@ -243,12 +242,12 @@ impl TcpListener { pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port()) - .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"accept failed"))?; + .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "accept failed"))?; let saddr = match ipaddr { Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port), Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port), _ => { - return Err(io::Error::new_const(ErrorKind::Uncategorized, &"accept failed")); + return Err(io::const_io_error!(ErrorKind::Uncategorized, "accept failed")); } }; diff --git a/library/std/src/sys/hermit/stdio.rs b/library/std/src/sys/hermit/stdio.rs index 33b8390431..514de1df6f 100644 --- a/library/std/src/sys/hermit/stdio.rs +++ b/library/std/src/sys/hermit/stdio.rs @@ -40,7 +40,7 @@ impl io::Write for Stdout { unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } if len < 0 { - Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stdout is not able to print")) + Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) } else { Ok(len as usize) } @@ -52,7 +52,7 @@ impl io::Write for Stdout { unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } if len < 0 { - Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stdout is not able to print")) + Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) } else { Ok(len as usize) } @@ -81,7 +81,7 @@ impl io::Write for Stderr { unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } if len < 0 { - Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stderr is not able to print")) + Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) } else { Ok(len as usize) } @@ -93,7 +93,7 @@ impl io::Write for Stderr { unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } if len < 0 { - Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stderr is not able to print")) + Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) } else { Ok(len as usize) } diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs index 81b21fbbb1..e53a1fea6a 100644 --- a/library/std/src/sys/hermit/thread.rs +++ b/library/std/src/sys/hermit/thread.rs @@ -39,7 +39,7 @@ impl Thread { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. drop(Box::from_raw(p)); - Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Unable to create thread!")) + Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Unable to create thread!")) } else { Ok(Thread { tid: tid }) }; diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs index c02de17c1f..27173de630 100644 --- a/library/std/src/sys/hermit/time.rs +++ b/library/std/src/sys/hermit/time.rs @@ -115,14 +115,6 @@ impl Instant { Instant { t: time } } - pub const fn zero() -> Instant { - Instant { t: Timespec::zero() } - } - - pub fn actually_monotonic() -> bool { - true - } - pub fn checked_sub_instant(&self, other: &Instant) -> Option { self.t.sub_timespec(&other.t).ok() } diff --git a/library/std/src/sys/itron/condvar.rs b/library/std/src/sys/itron/condvar.rs index dac4b8abfc..2992a6a542 100644 --- a/library/std/src/sys/itron/condvar.rs +++ b/library/std/src/sys/itron/condvar.rs @@ -15,10 +15,12 @@ unsafe impl Sync for Condvar {} pub type MovableCondvar = Condvar; impl Condvar { + #[inline] pub const fn new() -> Condvar { Condvar { waiters: SpinMutex::new(waiter_queue::WaiterQueue::new()) } } + #[inline] pub unsafe fn init(&mut self) {} pub unsafe fn notify_one(&self) { @@ -190,7 +192,7 @@ mod waiter_queue { let insert_after = { let mut cursor = head.last; loop { - if waiter.priority <= cursor.as_ref().priority { + if waiter.priority >= cursor.as_ref().priority { // `cursor` and all previous waiters have the same or higher // priority than `current_task_priority`. Insert the new // waiter right after `cursor`. @@ -206,7 +208,7 @@ mod waiter_queue { if let Some(mut insert_after) = insert_after { // Insert `waiter` after `insert_after` - let insert_before = insert_after.as_ref().prev; + let insert_before = insert_after.as_ref().next; waiter.prev = Some(insert_after); insert_after.as_mut().next = Some(waiter_ptr); @@ -214,6 +216,8 @@ mod waiter_queue { waiter.next = insert_before; if let Some(mut insert_before) = insert_before { insert_before.as_mut().prev = Some(waiter_ptr); + } else { + head.last = waiter_ptr; } } else { // Insert `waiter` to the front @@ -240,11 +244,11 @@ mod waiter_queue { match (waiter.prev, waiter.next) { (Some(mut prev), Some(mut next)) => { prev.as_mut().next = Some(next); - next.as_mut().next = Some(prev); + next.as_mut().prev = Some(prev); } (None, Some(mut next)) => { head.first = next; - next.as_mut().next = None; + next.as_mut().prev = None; } (Some(mut prev), None) => { prev.as_mut().next = None; @@ -271,6 +275,7 @@ mod waiter_queue { unsafe { waiter.as_ref().task != 0 } } + #[inline] pub fn pop_front(&mut self) -> Option { unsafe { let head = self.head.as_mut()?; diff --git a/library/std/src/sys/itron/thread.rs b/library/std/src/sys/itron/thread.rs index ebcc9ab26e..5b718a460d 100644 --- a/library/std/src/sys/itron/thread.rs +++ b/library/std/src/sys/itron/thread.rs @@ -77,17 +77,14 @@ const LIFECYCLE_DETACHED_OR_JOINED: usize = usize::MAX; const LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE: usize = usize::MAX; // there's no single value for `JOINING` -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * crate::mem::size_of::(); +// 64KiB for 32-bit ISAs, 128KiB for 64-bit ISAs. +pub const DEFAULT_MIN_STACK_SIZE: usize = 0x4000 * crate::mem::size_of::(); impl Thread { /// # Safety /// /// See `thread::Builder::spawn_unchecked` for safety requirements. pub unsafe fn new(stack: usize, p: Box) -> io::Result { - // Inherit the current task's priority - let current_task = task::try_current_task_id().map_err(|e| e.as_io_error())?; - let priority = task::try_task_priority(current_task).map_err(|e| e.as_io_error())?; - let inner = Box::new(ThreadInner { start: UnsafeCell::new(ManuallyDrop::new(p)), lifecycle: AtomicUsize::new(LIFECYCLE_INIT), @@ -175,7 +172,8 @@ impl Thread { exinf: inner_ptr as abi::EXINF, // The entry point task: Some(trampoline), - itskpri: priority, + // Inherit the calling task's base priority + itskpri: abi::TPRI_SELF, stksz: stack, // Let the kernel allocate the stack, stk: crate::ptr::null_mut(), diff --git a/library/std/src/sys/itron/time.rs b/library/std/src/sys/itron/time.rs index 6a992ad1d3..25f13ee441 100644 --- a/library/std/src/sys/itron/time.rs +++ b/library/std/src/sys/itron/time.rs @@ -14,15 +14,6 @@ impl Instant { } } - pub const fn zero() -> Instant { - Instant(0) - } - - pub fn actually_monotonic() -> bool { - // There are ways to change the system time - false - } - pub fn checked_sub_instant(&self, other: &Instant) -> Option { self.0.checked_sub(other.0).map(|ticks| { // `SYSTIM` is measured in microseconds diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs index a2a763c2b4..158c92e7a7 100644 --- a/library/std/src/sys/sgx/mod.rs +++ b/library/std/src/sys/sgx/mod.rs @@ -58,7 +58,7 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::Error::new_const(ErrorKind::Unsupported, &"operation not supported on SGX yet") + crate::io::const_io_error!(ErrorKind::Unsupported, "operation not supported on SGX yet") } /// This function is used to implement various functions that doesn't exist, @@ -69,9 +69,9 @@ pub fn unsupported_err() -> crate::io::Error { pub fn sgx_ineffective(v: T) -> crate::io::Result { static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false); if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { - Err(crate::io::Error::new_const( + Err(crate::io::const_io_error!( ErrorKind::Uncategorized, - &"operation can't be trusted to have any effect on SGX", + "operation can't be trusted to have any effect on SGX", )) } else { Ok(v) diff --git a/library/std/src/sys/sgx/net.rs b/library/std/src/sys/sgx/net.rs index 89c5af6124..d14990c687 100644 --- a/library/std/src/sys/sgx/net.rs +++ b/library/std/src/sys/sgx/net.rs @@ -97,9 +97,9 @@ impl TcpStream { pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result { if dur == Duration::default() { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } Self::connect(Ok(addr)) // FIXME: ignoring timeout @@ -108,9 +108,9 @@ impl TcpStream { pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { match dur { Some(dur) if dur == Duration::default() => { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } _ => sgx_ineffective(()), @@ -120,9 +120,9 @@ impl TcpStream { pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { match dur { Some(dur) if dur == Duration::default() => { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } _ => sgx_ineffective(()), diff --git a/library/std/src/sys/sgx/path.rs b/library/std/src/sys/sgx/path.rs index 840a7ae042..c805c15e70 100644 --- a/library/std/src/sys/sgx/path.rs +++ b/library/std/src/sys/sgx/path.rs @@ -1,5 +1,7 @@ use crate::ffi::OsStr; -use crate::path::Prefix; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::unsupported; #[inline] pub fn is_sep_byte(b: u8) -> bool { @@ -17,3 +19,7 @@ pub fn parse_prefix(_: &OsStr) -> Option> { pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; + +pub(crate) fn absolute(_path: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/sgx/time.rs b/library/std/src/sys/sgx/time.rs index e2f6e6dba6..db4cf2804b 100644 --- a/library/std/src/sys/sgx/time.rs +++ b/library/std/src/sys/sgx/time.rs @@ -25,14 +25,6 @@ impl Instant { pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(Instant(self.0.checked_sub(*other)?)) } - - pub fn actually_monotonic() -> bool { - false - } - - pub const fn zero() -> Instant { - Instant(Duration::from_secs(0)) - } } impl SystemTime { diff --git a/library/std/src/sys/solid/abi/sockets.rs b/library/std/src/sys/solid/abi/sockets.rs index 7c21d0dd25..eb06a6dd92 100644 --- a/library/std/src/sys/solid/abi/sockets.rs +++ b/library/std/src/sys/solid/abi/sockets.rs @@ -175,6 +175,9 @@ extern "C" { #[link_name = "SOLID_NET_Close"] pub fn close(s: c_int) -> c_int; + #[link_name = "SOLID_NET_Dup"] + pub fn dup(s: c_int) -> c_int; + #[link_name = "SOLID_NET_GetPeerName"] pub fn getpeername(s: c_int, name: *mut sockaddr, namelen: *mut socklen_t) -> c_int; diff --git a/library/std/src/sys/solid/fs.rs b/library/std/src/sys/solid/fs.rs index 8a0eeff0c4..a2cbee4dcf 100644 --- a/library/std/src/sys/solid/fs.rs +++ b/library/std/src/sys/solid/fs.rs @@ -289,7 +289,26 @@ impl OpenOptions { } fn cstr(path: &Path) -> io::Result { - Ok(CString::new(path.as_os_str().as_bytes())?) + let path = path.as_os_str().as_bytes(); + + if !path.starts_with(br"\") { + // Relative paths aren't supported + return Err(crate::io::const_io_error!( + crate::io::ErrorKind::Unsupported, + "relative path is not supported on this platform", + )); + } + + // Apply the thread-safety wrapper + const SAFE_PREFIX: &[u8] = br"\TS"; + let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat(); + + CString::from_vec_with_nul(wrapped_path).map_err(|_| { + crate::io::const_io_error!( + io::ErrorKind::InvalidInput, + "path provided contains a nul byte", + ) + }) } impl File { @@ -461,7 +480,7 @@ impl fmt::Debug for File { pub fn unlink(p: &Path) -> io::Result<()> { if stat(p)?.file_type().is_dir() { - Err(io::Error::new_const(io::ErrorKind::IsADirectory, &"is a directory")) + Err(io::const_io_error!(io::ErrorKind::IsADirectory, "is a directory")) } else { error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) .map_err(|e| e.as_io_error())?; @@ -491,7 +510,7 @@ pub fn rmdir(p: &Path) -> io::Result<()> { .map_err(|e| e.as_io_error())?; Ok(()) } else { - Err(io::Error::new_const(io::ErrorKind::NotADirectory, &"not a directory")) + Err(io::const_io_error!(io::ErrorKind::NotADirectory, "not a directory")) } } @@ -511,7 +530,7 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { pub fn readlink(p: &Path) -> io::Result { // This target doesn't support symlinks stat(p)?; - Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"not a symbolic link")) + Err(io::const_io_error!(io::ErrorKind::InvalidInput, "not a symbolic link")) } pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 211b8d7de3..2082c94015 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -57,9 +57,9 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::Error::new_const( + crate::io::const_io_error!( crate::io::ErrorKind::Unsupported, - &"operation not supported on this platform", + "operation not supported on this platform", ) } diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs index 63ba6341c7..a43407bd0f 100644 --- a/library/std/src/sys/solid/net.rs +++ b/library/std/src/sys/solid/net.rs @@ -107,7 +107,7 @@ impl FileDesc { } fn duplicate(&self) -> io::Result { - super::unsupported() + cvt(unsafe { netc::dup(self.fd) }).map(Self::new) } } @@ -243,9 +243,9 @@ impl Socket { } if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } @@ -271,7 +271,7 @@ impl Socket { }; match n { - 0 => Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out")), + 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")), _ => { let can_write = writefds.num_fds != 0; if !can_write { @@ -364,9 +364,9 @@ impl Socket { let timeout = match dur { Some(dur) => { if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs index 82542d81e6..22239e1fa8 100644 --- a/library/std/src/sys/solid/os.rs +++ b/library/std/src/sys/solid/os.rs @@ -173,11 +173,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { /// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this /// function just returns a generic error. fn cvt_env(t: c_int) -> io::Result { - if t == -1 { - Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"failure")) - } else { - Ok(t) - } + if t == -1 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/solid/path.rs b/library/std/src/sys/solid/path.rs index 4a14332d49..7045c9be25 100644 --- a/library/std/src/sys/solid/path.rs +++ b/library/std/src/sys/solid/path.rs @@ -1,5 +1,7 @@ use crate::ffi::OsStr; -use crate::path::Prefix; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::unsupported; #[inline] pub fn is_sep_byte(b: u8) -> bool { @@ -17,3 +19,7 @@ pub fn parse_prefix(_: &OsStr) -> Option> { pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; + +pub(crate) fn absolute(_path: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/solid/time.rs b/library/std/src/sys/solid/time.rs index c67a736a90..ab988be244 100644 --- a/library/std/src/sys/solid/time.rs +++ b/library/std/src/sys/solid/time.rs @@ -21,7 +21,7 @@ impl SystemTime { tm_min: rtc.tm_min, tm_hour: rtc.tm_hour, tm_mday: rtc.tm_mday, - tm_mon: rtc.tm_mon, + tm_mon: rtc.tm_mon - 1, tm_year: rtc.tm_year, tm_wday: rtc.tm_wday, tm_yday: 0, diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 2362bff913..3de7c68a68 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -259,22 +259,9 @@ impl FileDesc { } } + #[inline] pub fn duplicate(&self) -> io::Result { - // We want to atomically duplicate this file descriptor and set the - // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This - // is a POSIX flag that was added to Linux in 2.6.24. - #[cfg(not(target_os = "espidf"))] - let cmd = libc::F_DUPFD_CLOEXEC; - - // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics - // will never be supported, as this is a bare metal framework with - // no capabilities for multi-process execution. While F_DUPFD is also - // not supported yet, it might be (currently it returns ENOSYS). - #[cfg(target_os = "espidf")] - let cmd = libc::F_DUPFD; - - let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?; - Ok(unsafe { FileDesc::from_raw_fd(fd) }) + Ok(Self(self.0.try_clone()?)) } } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index f8deda93fe..8bd0b9b14a 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -34,7 +34,20 @@ use libc::c_char; use libc::dirfd; #[cfg(any(target_os = "linux", target_os = "emscripten"))] use libc::fstatat64; +#[cfg(any( + target_os = "android", + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos" +))] +use libc::readdir as readdir64; +#[cfg(target_os = "linux")] +use libc::readdir64; +#[cfg(any(target_os = "emscripten", target_os = "l4re"))] +use libc::readdir64_r; #[cfg(not(any( + target_os = "android", target_os = "linux", target_os = "emscripten", target_os = "solaris", @@ -60,9 +73,7 @@ use libc::{ lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, }; #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))] -use libc::{ - dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64, -}; +use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; pub use crate::sys_common::fs::try_exists; @@ -202,6 +213,8 @@ struct InnerReadDir { pub struct ReadDir { inner: Arc, #[cfg(not(any( + target_os = "android", + target_os = "linux", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", @@ -218,11 +231,12 @@ unsafe impl Sync for Dir {} pub struct DirEntry { entry: dirent64, dir: Arc, - // We need to store an owned copy of the entry name - // on Solaris and Fuchsia because a) it uses a zero-length - // array to store the name, b) its lifetime between readdir - // calls is not guaranteed. + // We need to store an owned copy of the entry name on platforms that use + // readdir() (not readdir_r()), because a) struct dirent may use a flexible + // array to store the name, b) it lives only until the next readdir() call. #[cfg(any( + target_os = "android", + target_os = "linux", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", @@ -373,17 +387,17 @@ impl FileAttr { tv_nsec: ext.stx_btime.tv_nsec as _, })) } else { - Err(io::Error::new_const( + Err(io::const_io_error!( io::ErrorKind::Uncategorized, - &"creation time is not available for the filesystem", + "creation time is not available for the filesystem", )) }; } } - Err(io::Error::new_const( + Err(io::const_io_error!( io::ErrorKind::Unsupported, - &"creation time is not available on this platform \ + "creation time is not available on this platform \ currently", )) } @@ -449,6 +463,8 @@ impl Iterator for ReadDir { type Item = io::Result; #[cfg(any( + target_os = "android", + target_os = "linux", target_os = "solaris", target_os = "fuchsia", target_os = "redox", @@ -457,12 +473,13 @@ impl Iterator for ReadDir { fn next(&mut self) -> Option> { unsafe { loop { - // Although readdir_r(3) would be a correct function to use here because - // of the thread safety, on Illumos and Fuchsia the readdir(3C) function - // is safe to use in threaded applications and it is generally preferred - // over the readdir_r(3C) function. + // As of POSIX.1-2017, readdir() is not required to be thread safe; only + // readdir_r() is. However, readdir_r() cannot correctly handle platforms + // with unlimited or variable NAME_MAX. Many modern platforms guarantee + // thread safety for readdir() as long an individual DIR* is not accessed + // concurrently, which is sufficient for Rust. super::os::set_errno(0); - let entry_ptr = libc::readdir(self.inner.dirp.0); + let entry_ptr = readdir64(self.inner.dirp.0); if entry_ptr.is_null() { // null can mean either the end is reached or an error occurred. // So we had to clear errno beforehand to check for an error now. @@ -472,10 +489,18 @@ impl Iterator for ReadDir { }; } + // Only d_reclen bytes of *entry_ptr are valid, so we can't just copy the + // whole thing (#93384). Instead, copy everything except the name. + let entry_bytes = entry_ptr as *const u8; + let entry_name = ptr::addr_of!((*entry_ptr).d_name) as *const u8; + let name_offset = entry_name.offset_from(entry_bytes) as usize; + let mut entry: dirent64 = mem::zeroed(); + ptr::copy_nonoverlapping(entry_bytes, &mut entry as *mut _ as *mut u8, name_offset); + let ret = DirEntry { - entry: *entry_ptr, + entry, // d_name is guaranteed to be null-terminated. - name: CStr::from_ptr((*entry_ptr).d_name.as_ptr()).to_owned(), + name: CStr::from_ptr(entry_name as *const _).to_owned(), dir: Arc::clone(&self.inner), }; if ret.name_bytes() != b"." && ret.name_bytes() != b".." { @@ -486,6 +511,8 @@ impl Iterator for ReadDir { } #[cfg(not(any( + target_os = "android", + target_os = "linux", target_os = "solaris", target_os = "fuchsia", target_os = "redox", @@ -531,17 +558,17 @@ impl Drop for Dir { impl DirEntry { pub fn path(&self) -> PathBuf { - self.dir.root.join(OsStr::from_bytes(self.name_bytes())) + self.dir.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { - OsStr::from_bytes(self.name_bytes()).to_os_string() + self.file_name_os_str().to_os_string() } #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] pub fn metadata(&self) -> io::Result { let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; - let name = self.entry.d_name.as_ptr(); + let name = self.name_cstr().as_ptr(); cfg_has_statx! { if let Some(ret) = unsafe { try_statx( @@ -571,7 +598,7 @@ impl DirEntry { target_os = "vxworks" ))] pub fn file_type(&self) -> io::Result { - lstat(&self.path()).map(|m| m.file_type()) + self.metadata().map(|m| m.file_type()) } #[cfg(not(any( @@ -589,7 +616,7 @@ impl DirEntry { libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), - _ => lstat(&self.path()).map(|m| m.file_type()), + _ => self.metadata().map(|m| m.file_type()), } } @@ -639,29 +666,21 @@ impl DirEntry { ) } } - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "emscripten", - target_os = "l4re", - target_os = "haiku", - target_os = "vxworks", - target_os = "espidf" - ))] - fn name_bytes(&self) -> &[u8] { - unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() } - } - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox" - ))] + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "netbsd", + target_os = "openbsd", + target_os = "freebsd", + target_os = "dragonfly" + )))] fn name_bytes(&self) -> &[u8] { - self.name.as_bytes() + self.name_cstr().to_bytes() } #[cfg(not(any( + target_os = "android", + target_os = "linux", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", @@ -670,7 +689,14 @@ impl DirEntry { fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } } - #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "fuchsia"))] + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox" + ))] fn name_cstr(&self) -> &CStr { &self.name } @@ -1076,6 +1102,8 @@ pub fn readdir(p: &Path) -> io::Result { Ok(ReadDir { inner: Arc::new(inner), #[cfg(not(any( + target_os = "android", + target_os = "linux", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", @@ -1448,8 +1476,8 @@ pub fn chroot(dir: &Path) -> io::Result<()> { pub use remove_dir_impl::remove_dir_all; -// Fallback for REDOX -#[cfg(target_os = "redox")] +// Fallback for REDOX and ESP-IDF +#[cfg(any(target_os = "redox", target_os = "espidf"))] mod remove_dir_impl { pub use crate::sys_common::fs::remove_dir_all; } @@ -1573,7 +1601,11 @@ mod remove_dir_impl { } // Modern implementation using openat(), unlinkat() and fdopendir() -#[cfg(not(any(all(target_os = "macos", target_arch = "x86_64"), target_os = "redox")))] +#[cfg(not(any( + all(target_os = "macos", target_arch = "x86_64"), + target_os = "redox", + target_os = "espidf" +)))] mod remove_dir_impl { use super::{cstr, lstat, Dir, DirEntry, InnerReadDir, ReadDir}; use crate::ffi::CStr; @@ -1611,6 +1643,8 @@ mod remove_dir_impl { ReadDir { inner: Arc::new(InnerReadDir { dirp, root: dummy_root }), #[cfg(not(any( + target_os = "android", + target_os = "linux", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", @@ -1627,7 +1661,6 @@ mod remove_dir_impl { target_os = "illumos", target_os = "haiku", target_os = "vxworks", - target_os = "fuchsia" ))] fn is_dir(_ent: &DirEntry) -> Option { None @@ -1638,7 +1671,6 @@ mod remove_dir_impl { target_os = "illumos", target_os = "haiku", target_os = "vxworks", - target_os = "fuchsia" )))] fn is_dir(ent: &DirEntry) -> Option { match ent.entry.d_type { diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index ba63b41534..d13e1ecbbf 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -1,8 +1,8 @@ macro_rules! unimpl { () => { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::Unsupported, - &"No networking available on L4Re.", + "No networking available on L4Re.", )); }; } diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 2ba6c8d830..605cc499b3 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -159,7 +159,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ENOSPC => StorageFull, libc::ENOSYS => Unsupported, libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => FilenameTooLong, + libc::ENAMETOOLONG => InvalidFilename, libc::ENETDOWN => NetworkDown, libc::ENETUNREACH => NetworkUnreachable, libc::ENOTCONN => NotConnected, @@ -322,9 +322,6 @@ mod unsupported { } pub fn unsupported_err() -> io::Error { - io::Error::new_const( - io::ErrorKind::Unsupported, - &"operation not supported on this platform", - ) + io::const_io_error!(io::ErrorKind::Unsupported, "operation not supported on this platform",) } } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index a82a017212..61c15ecd85 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -154,9 +154,9 @@ impl Socket { let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } @@ -165,7 +165,7 @@ impl Socket { loop { let elapsed = start.elapsed(); if elapsed >= timeout { - return Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out")); + return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); } let timeout = timeout - elapsed; @@ -192,9 +192,9 @@ impl Socket { // for POLLHUP rather than read readiness if pollfd.revents & libc::POLLHUP != 0 { let e = self.take_error()?.unwrap_or_else(|| { - io::Error::new_const( + io::const_io_error!( io::ErrorKind::Uncategorized, - &"no error set after POLLHUP", + "no error set after POLLHUP", ) }); return Err(e); @@ -338,9 +338,9 @@ impl Socket { let timeout = match dur { Some(dur) => { if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 8a028d9930..b268ef5c36 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -75,7 +75,7 @@ pub fn errno() -> i32 { } /// Sets the platform-specific value of errno -#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall! +#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall! #[allow(dead_code)] // but not all target cfgs actually end up using it pub fn set_errno(e: i32) { unsafe { *errno_location() = e as c_int } @@ -294,9 +294,9 @@ pub fn current_exe() -> io::Result { 0, ))?; if path_len <= 1 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::Uncategorized, - &"KERN_PROC_PATHNAME sysctl returned zero-length string", + "KERN_PROC_PATHNAME sysctl returned zero-length string", )); } let mut path: Vec = Vec::with_capacity(path_len); @@ -317,9 +317,9 @@ pub fn current_exe() -> io::Result { if curproc_exe.is_file() { return crate::fs::read_link(curproc_exe); } - Err(io::Error::new_const( + Err(io::const_io_error!( io::ErrorKind::Uncategorized, - &"/proc/curproc/exe doesn't point to regular file.", + "/proc/curproc/exe doesn't point to regular file.", )) } sysctl().or_else(|_| procfs()) @@ -336,9 +336,9 @@ pub fn current_exe() -> io::Result { cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?; argv.set_len(argv_len as usize); if argv[0].is_null() { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::Uncategorized, - &"no current exe available", + "no current exe available", )); } let argv0 = CStr::from_ptr(argv[0]).to_bytes(); @@ -353,9 +353,9 @@ pub fn current_exe() -> io::Result { #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] pub fn current_exe() -> io::Result { match crate::fs::read_link("/proc/self/exe") { - Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new_const( + Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_io_error!( io::ErrorKind::Uncategorized, - &"no /proc/self/exe available. Is /proc mounted?", + "no /proc/self/exe available. Is /proc mounted?", )), other => other, } @@ -417,7 +417,7 @@ pub fn current_exe() -> io::Result { ); if result != 0 { use crate::io::ErrorKind; - Err(io::Error::new_const(ErrorKind::Uncategorized, &"Error getting executable path")) + Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path")) } else { let name = CStr::from_ptr((*info.as_ptr()).name.as_ptr()).to_bytes(); Ok(PathBuf::from(OsStr::from_bytes(name))) @@ -433,7 +433,7 @@ pub fn current_exe() -> io::Result { #[cfg(any(target_os = "fuchsia", target_os = "l4re"))] pub fn current_exe() -> io::Result { use crate::io::ErrorKind; - Err(io::Error::new_const(ErrorKind::Unsupported, &"Not yet implemented!")) + Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!")) } #[cfg(target_os = "vxworks")] diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs index 717add9ec4..6d6f4c8b8d 100644 --- a/library/std/src/sys/unix/path.rs +++ b/library/std/src/sys/unix/path.rs @@ -1,5 +1,7 @@ +use crate::env; use crate::ffi::OsStr; -use crate::path::Prefix; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; #[inline] pub fn is_sep_byte(b: u8) -> bool { @@ -18,3 +20,43 @@ pub fn parse_prefix(_: &OsStr) -> Option> { pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; + +/// Make a POSIX path absolute without changing its semantics. +pub(crate) fn absolute(path: &Path) -> io::Result { + // This is mostly a wrapper around collecting `Path::components`, with + // exceptions made where this conflicts with the POSIX specification. + // See 4.13 Pathname Resolution, IEEE Std 1003.1-2017 + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + + let mut components = path.components(); + let path_os = path.as_os_str().bytes(); + + let mut normalized = if path.is_absolute() { + // "If a pathname begins with two successive characters, the + // first component following the leading characters may be + // interpreted in an implementation-defined manner, although more than + // two leading characters shall be treated as a single + // character." + if path_os.starts_with(b"//") && !path_os.starts_with(b"///") { + components.next(); + PathBuf::from("//") + } else { + PathBuf::new() + } + } else { + env::current_dir()? + }; + normalized.extend(components); + + // "Interfaces using pathname resolution may specify additional constraints + // when a pathname that does not name an existing directory contains at + // least one non- character and contains one or more trailing + // characters". + // A trailing is also meaningful if "a symbolic link is + // encountered during pathname resolution". + if path_os.ends_with(b"/") { + normalized.push(""); + } + + Ok(normalized) +} diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 7ac2f9d8af..97985ddd33 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -476,6 +476,12 @@ impl ExitCode { } } +impl From for ExitCode { + fn from(code: u8) -> Self { + Self(code) + } +} + pub struct CommandArgs<'a> { iter: crate::slice::Iter<'a, CString>, } diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index ce77c210a6..09bfd9680f 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -23,9 +23,9 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"nul byte found in provided data", + "nul byte found in provided data", )); } @@ -38,9 +38,9 @@ impl Command { pub fn exec(&mut self, default: Stdio) -> io::Error { if self.saw_nul() { - return io::Error::new_const( + return io::const_io_error!( io::ErrorKind::InvalidInput, - &"nul byte found in provided data", + "nul byte found in provided data", ); } @@ -186,9 +186,9 @@ impl Process { ))?; } if actual != 1 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, - &"Failed to get exit status of process", + "Failed to get exit status of process", )); } Ok(ExitStatus(proc_info.return_code)) @@ -224,9 +224,9 @@ impl Process { ))?; } if actual != 1 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, - &"Failed to get exit status of process", + "Failed to get exit status of process", )); } Ok(Some(ExitStatus(proc_info.return_code))) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index bce35b380e..9fc2d9fce4 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -44,9 +44,9 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::Error::new_const( + return Err(io::const_io_error!( ErrorKind::InvalidInput, - &"nul byte found in provided data", + "nul byte found in provided data", )); } @@ -222,10 +222,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return io::Error::new_const( - ErrorKind::InvalidInput, - &"nul byte found in provided data", - ); + return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",); } match self.setup_io(default, true) { @@ -581,9 +578,9 @@ impl Process { // and used for another process, and we probably shouldn't be killing // random processes, so just return an error. if self.status.is_some() { - Err(Error::new_const( + Err(io::const_io_error!( ErrorKind::InvalidInput, - &"invalid argument: can't kill an exited process", + "invalid argument: can't kill an exited process", )) } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs index 157debf2d2..560c62155d 100644 --- a/library/std/src/sys/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/unix/process/process_unix/tests.rs @@ -53,5 +53,10 @@ fn test_command_fork_no_unwind() { let status = got.expect("panic unexpectedly propagated"); dbg!(status); let signal = status.signal().expect("expected child process to die of signal"); - assert!(signal == libc::SIGABRT || signal == libc::SIGILL || signal == libc::SIGTRAP); + assert!( + signal == libc::SIGABRT + || signal == libc::SIGILL + || signal == libc::SIGTRAP + || signal == libc::SIGSEGV + ); } diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs index c17822f512..c6714d3aae 100644 --- a/library/std/src/sys/unix/process/process_vxworks.rs +++ b/library/std/src/sys/unix/process/process_vxworks.rs @@ -24,9 +24,9 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::Error::new_const( + return Err(io::const_io_error!( ErrorKind::InvalidInput, - &"nul byte found in provided data", + "nul byte found in provided data", )); } let (ours, theirs) = self.setup_io(default, needs_stdin)?; @@ -142,9 +142,9 @@ impl Process { // and used for another process, and we probably shouldn't be killing // random processes, so just return an error. if self.status.is_some() { - Err(Error::new_const( + Err(io::const_io_error!( ErrorKind::InvalidInput, - &"invalid argument: can't kill an exited process", + "invalid argument: can't kill an exited process", )) } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 9e02966b57..cf8cf5ad49 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -287,7 +287,7 @@ pub fn available_parallelism() -> io::Result { } match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { -1 => Err(io::Error::last_os_error()), - 0 => Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")), + 0 => Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")), cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }), } } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] { @@ -318,7 +318,7 @@ pub fn available_parallelism() -> io::Result { if res == -1 { return Err(io::Error::last_os_error()); } else if cpus == 0 { - return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")); + return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); } } Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }) @@ -344,7 +344,7 @@ pub fn available_parallelism() -> io::Result { if res == -1 { return Err(io::Error::last_os_error()); } else if cpus == 0 { - return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")); + return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); } Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }) @@ -356,14 +356,14 @@ pub fn available_parallelism() -> io::Result { let res = libc::get_system_info(&mut sinfo); if res != libc::B_OK { - return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")); + return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); } Ok(NonZeroUsize::new_unchecked(sinfo.cpu_count as usize)) } } else { // FIXME: implement on vxWorks, Redox, l4re - Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Getting the number of hardware threads is not supported on the target platform")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) } } } diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 824283ef6c..59ddd1aa92 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -154,14 +154,6 @@ mod inner { Instant { t: unsafe { mach_absolute_time() } } } - pub const fn zero() -> Instant { - Instant { t: 0 } - } - - pub fn actually_monotonic() -> bool { - true - } - pub fn checked_sub_instant(&self, other: &Instant) -> Option { let diff = self.t.checked_sub(other.t)?; let info = info(); @@ -296,17 +288,6 @@ mod inner { Instant { t: now(libc::CLOCK_MONOTONIC) } } - pub const fn zero() -> Instant { - Instant { t: Timespec::zero() } - } - - pub fn actually_monotonic() -> bool { - (cfg!(target_os = "linux") && cfg!(target_arch = "x86_64")) - || (cfg!(target_os = "linux") && cfg!(target_arch = "x86")) - || (cfg!(target_os = "linux") && cfg!(target_arch = "aarch64")) - || cfg!(target_os = "fuchsia") - } - pub fn checked_sub_instant(&self, other: &Instant) -> Option { self.t.sub_timespec(&other.t).ok() } diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs index a06b44e96a..5274f53a7d 100644 --- a/library/std/src/sys/unsupported/common.rs +++ b/library/std/src/sys/unsupported/common.rs @@ -21,9 +21,9 @@ pub fn unsupported() -> std_io::Result { } pub fn unsupported_err() -> std_io::Error { - std_io::Error::new_const( + std_io::const_io_error!( std_io::ErrorKind::Unsupported, - &"operation not supported on this platform", + "operation not supported on this platform", ) } diff --git a/library/std/src/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs index 2886ec1180..e150ae143a 100644 --- a/library/std/src/sys/unsupported/os.rs +++ b/library/std/src/sys/unsupported/os.rs @@ -81,11 +81,11 @@ pub fn getenv(_: &OsStr) -> Option { } pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot set env vars on this platform")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } pub fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot unset env vars on this platform")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs index 7846e43cfb..42a1ff730e 100644 --- a/library/std/src/sys/unsupported/process.rs +++ b/library/std/src/sys/unsupported/process.rs @@ -162,6 +162,15 @@ impl ExitCode { } } +impl From for ExitCode { + fn from(code: u8) -> Self { + match code { + 0 => Self::SUCCESS, + 1..=255 => Self::FAILURE, + } + } +} + pub struct Process(!); impl Process { diff --git a/library/std/src/sys/unsupported/time.rs b/library/std/src/sys/unsupported/time.rs index 8aaf1777f2..6d67b538a9 100644 --- a/library/std/src/sys/unsupported/time.rs +++ b/library/std/src/sys/unsupported/time.rs @@ -13,14 +13,6 @@ impl Instant { panic!("time not implemented on this platform") } - pub const fn zero() -> Instant { - Instant(Duration::from_secs(0)) - } - - pub fn actually_monotonic() -> bool { - false - } - pub fn checked_sub_instant(&self, other: &Instant) -> Option { self.0.checked_sub(other.0) } diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs index e4f4456611..0b9c8e61db 100644 --- a/library/std/src/sys/wasi/fd.rs +++ b/library/std/src/sys/wasi/fd.rs @@ -228,6 +228,10 @@ impl WasiFd { unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } + pub fn sock_accept(&self, flags: wasi::Fdflags) -> io::Result { + unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } + } + pub fn sock_recv( &self, ri_data: &mut [IoSliceMut<'_>], diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 5924789d12..cd6815bfc2 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -711,7 +711,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { pub fn osstr2str(f: &OsStr) -> io::Result<&str> { f.to_str() - .ok_or_else(|| io::Error::new_const(io::ErrorKind::Uncategorized, &"input must be utf-8")) + .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) } pub fn copy(from: &Path, to: &Path) -> io::Result { @@ -757,7 +757,7 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { for entry in ReadDir::new(fd, dummy_root) { let entry = entry?; let path = crate::str::from_utf8(&entry.name).map_err(|_| { - io::Error::new_const(io::ErrorKind::Uncategorized, &"invalid utf-8 file name found") + io::const_io_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") })?; if entry.file_type()?.is_dir() { diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index 8d62335aae..f878941939 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -61,23 +61,26 @@ pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { if errno > u16::MAX as i32 || errno < 0 { return Uncategorized; } - match errno as u16 { - wasi::ERRNO_CONNREFUSED => ConnectionRefused, - wasi::ERRNO_CONNRESET => ConnectionReset, - wasi::ERRNO_PERM | wasi::ERRNO_ACCES => PermissionDenied, - wasi::ERRNO_PIPE => BrokenPipe, - wasi::ERRNO_NOTCONN => NotConnected, - wasi::ERRNO_CONNABORTED => ConnectionAborted, - wasi::ERRNO_ADDRNOTAVAIL => AddrNotAvailable, - wasi::ERRNO_ADDRINUSE => AddrInUse, - wasi::ERRNO_NOENT => NotFound, - wasi::ERRNO_INTR => Interrupted, - wasi::ERRNO_INVAL => InvalidInput, - wasi::ERRNO_TIMEDOUT => TimedOut, - wasi::ERRNO_EXIST => AlreadyExists, - wasi::ERRNO_AGAIN => WouldBlock, - wasi::ERRNO_NOSYS => Unsupported, - wasi::ERRNO_NOMEM => OutOfMemory, + + match errno { + e if e == wasi::ERRNO_CONNREFUSED.raw().into() => ConnectionRefused, + e if e == wasi::ERRNO_CONNRESET.raw().into() => ConnectionReset, + e if e == wasi::ERRNO_PERM.raw().into() || e == wasi::ERRNO_ACCES.raw().into() => { + PermissionDenied + } + e if e == wasi::ERRNO_PIPE.raw().into() => BrokenPipe, + e if e == wasi::ERRNO_NOTCONN.raw().into() => NotConnected, + e if e == wasi::ERRNO_CONNABORTED.raw().into() => ConnectionAborted, + e if e == wasi::ERRNO_ADDRNOTAVAIL.raw().into() => AddrNotAvailable, + e if e == wasi::ERRNO_ADDRINUSE.raw().into() => AddrInUse, + e if e == wasi::ERRNO_NOENT.raw().into() => NotFound, + e if e == wasi::ERRNO_INTR.raw().into() => Interrupted, + e if e == wasi::ERRNO_INVAL.raw().into() => InvalidInput, + e if e == wasi::ERRNO_TIMEDOUT.raw().into() => TimedOut, + e if e == wasi::ERRNO_EXIST.raw().into() => AlreadyExists, + e if e == wasi::ERRNO_AGAIN.raw().into() => WouldBlock, + e if e == wasi::ERRNO_NOSYS.raw().into() => Unsupported, + e if e == wasi::ERRNO_NOMEM.raw().into() => OutOfMemory, _ => Uncategorized, } } @@ -96,6 +99,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { return ret; } -fn err2io(err: wasi::Error) -> std_io::Error { - std_io::Error::from_raw_os_error(err.raw_error().into()) +fn err2io(err: wasi::Errno) -> std_io::Error { + std_io::Error::from_raw_os_error(err.raw().into()) } diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs index a4dbb22537..c66e0e4d32 100644 --- a/library/std/src/sys/wasi/net.rs +++ b/library/std/src/sys/wasi/net.rs @@ -1,5 +1,6 @@ #![deny(unsafe_op_in_unsafe_fn)] +use super::err2io; use super::fd::WasiFd; use crate::convert::TryFrom; use crate::fmt; @@ -87,24 +88,24 @@ impl TcpStream { unsupported() } - pub fn read(&self, _: &mut [u8]) -> io::Result { - unsupported() + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(buf)]) } - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unsupported() + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.socket().as_inner().read(bufs) } pub fn is_read_vectored(&self) -> bool { true } - pub fn write(&self, _: &[u8]) -> io::Result { - unsupported() + pub fn write(&self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) } - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unsupported() + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.socket().as_inner().write(bufs) } pub fn is_write_vectored(&self) -> bool { @@ -155,8 +156,23 @@ impl TcpStream { unsupported() } - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() + pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { + let fdstat = unsafe { + wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? + }; + + let mut flags = fdstat.fs_flags; + + if state { + flags |= wasi::FDFLAGS_NONBLOCK; + } else { + flags &= !wasi::FDFLAGS_NONBLOCK; + } + + unsafe { + wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) + .map_err(err2io) + } } pub fn socket(&self) -> &Socket { @@ -194,7 +210,16 @@ impl TcpListener { } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unsupported() + let fd = unsafe { + wasi::sock_accept(self.as_inner().as_inner().as_raw_fd() as _, 0).map_err(err2io)? + }; + + Ok(( + TcpStream::from_inner(unsafe { Socket::from_raw_fd(fd as _) }), + // WASI has no concept of SocketAddr yet + // return an unspecified IPv4Addr + SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), + )) } pub fn duplicate(&self) -> io::Result { @@ -221,8 +246,23 @@ impl TcpListener { unsupported() } - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() + pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { + let fdstat = unsafe { + wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? + }; + + let mut flags = fdstat.fs_flags; + + if state { + flags |= wasi::FDFLAGS_NONBLOCK; + } else { + flags &= !wasi::FDFLAGS_NONBLOCK; + } + + unsafe { + wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) + .map_err(err2io) + } } pub fn socket(&self) -> &Socket { diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs index 2c8f394cd4..4cc0e4ed5a 100644 --- a/library/std/src/sys/wasi/stdio.rs +++ b/library/std/src/sys/wasi/stdio.rs @@ -104,7 +104,7 @@ impl io::Write for Stderr { pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(wasi::ERRNO_BADF.into()) + err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into()) } pub fn panic_output() -> Option { diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs index 2e4e474c44..e7a6ab4be8 100644 --- a/library/std/src/sys/wasi/thread.rs +++ b/library/std/src/sys/wasi/thread.rs @@ -41,8 +41,7 @@ impl Thread { let in_ = wasi::Subscription { userdata: USERDATA, - r#type: wasi::EVENTTYPE_CLOCK, - u: wasi::SubscriptionU { clock }, + u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, }; unsafe { let mut event: wasi::Event = mem::zeroed(); @@ -51,7 +50,10 @@ impl Thread { ( Ok(1), wasi::Event { - userdata: USERDATA, error: 0, r#type: wasi::EVENTTYPE_CLOCK, .. + userdata: USERDATA, + error: wasi::ERRNO_SUCCESS, + type_: wasi::EVENTTYPE_CLOCK, + .. }, ) => {} _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs index 2e720d1160..088585654b 100644 --- a/library/std/src/sys/wasi/time.rs +++ b/library/std/src/sys/wasi/time.rs @@ -10,7 +10,7 @@ pub struct SystemTime(Duration); pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); -fn current_time(clock: u32) -> Duration { +fn current_time(clock: wasi::Clockid) -> Duration { let ts = unsafe { wasi::clock_time_get( clock, 1, // precision... seems ignored though? @@ -25,14 +25,6 @@ impl Instant { Instant(current_time(wasi::CLOCKID_MONOTONIC)) } - pub const fn zero() -> Instant { - Instant(Duration::from_secs(0)) - } - - pub fn actually_monotonic() -> bool { - true - } - pub fn checked_sub_instant(&self, other: &Instant) -> Option { self.0.checked_sub(other.0) } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 09d3661e4f..c7b6290693 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -83,11 +83,13 @@ pub const CSTR_GREATER_THAN: c_int = 3; pub const FILE_ATTRIBUTE_READONLY: DWORD = 0x1; pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10; pub const FILE_ATTRIBUTE_REPARSE_POINT: DWORD = 0x400; +pub const INVALID_FILE_ATTRIBUTES: DWORD = DWORD::MAX; pub const FILE_SHARE_DELETE: DWORD = 0x4; pub const FILE_SHARE_READ: DWORD = 0x1; pub const FILE_SHARE_WRITE: DWORD = 0x2; +pub const FILE_OPEN: ULONG = 0x00000001; pub const FILE_OPEN_REPARSE_POINT: ULONG = 0x200000; pub const OBJ_DONT_REPARSE: ULONG = 0x1000; @@ -1074,6 +1076,7 @@ extern "system" { lpBuffer: LPWSTR, lpFilePart: *mut LPWSTR, ) -> DWORD; + pub fn GetFileAttributesW(lpFileName: LPCWSTR) -> DWORD; } #[link(name = "ws2_32")] @@ -1228,15 +1231,20 @@ compat_fn! { compat_fn! { "ntdll": - pub fn NtOpenFile( + pub fn NtCreateFile( FileHandle: *mut HANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: *const OBJECT_ATTRIBUTES, IoStatusBlock: *mut IO_STATUS_BLOCK, + AllocationSize: *mut i64, + FileAttributes: ULONG, ShareAccess: ULONG, - OpenOptions: ULONG + CreateDisposition: ULONG, + CreateOptions: ULONG, + EaBuffer: *mut c_void, + EaLength: ULONG ) -> NTSTATUS { - panic!("`NtOpenFile` not available"); + panic!("`NtCreateFile` not available"); } pub fn RtlNtStatusToDosError( Status: NTSTATUS diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index dd21c6b438..cb83ee2469 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -460,7 +460,7 @@ impl File { } pub fn duplicate(&self) -> io::Result { - Ok(File { handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)? }) + Ok(Self { handle: self.handle.try_clone()? }) } fn reparse_point<'a>( @@ -511,9 +511,9 @@ impl File { ) } _ => { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::Uncategorized, - &"Unsupported reparse point type", + "Unsupported reparse point type", )); } }; @@ -712,11 +712,11 @@ impl<'a> Iterator for DirBuffIter<'a> { /// Open a link relative to the parent directory, ensure no symlinks are followed. fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result { - // This is implemented using the lower level `NtOpenFile` function as + // This is implemented using the lower level `NtCreateFile` function as // unfortunately opening a file relative to a parent is not supported by // win32 functions. It is however a fundamental feature of the NT kernel. // - // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntopenfile + // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile unsafe { let mut handle = ptr::null_mut(); let mut io_status = c::IO_STATUS_BLOCK::default(); @@ -732,14 +732,19 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result< Attributes: ATTRIBUTES.load(Ordering::Relaxed), ..c::OBJECT_ATTRIBUTES::default() }; - let status = c::NtOpenFile( + let status = c::NtCreateFile( &mut handle, access, &object, &mut io_status, + crate::ptr::null_mut(), + 0, c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE, + c::FILE_OPEN, // If `name` is a symlink then open the link rather than the target. c::FILE_OPEN_REPARSE_POINT, + crate::ptr::null_mut(), + 0, ); // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError") if c::nt_success(status) { @@ -1124,9 +1129,9 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { #[cfg(target_vendor = "uwp")] pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::Unsupported, - &"hard link are not supported on UWP", + "hard link are not supported on UWP", )); } diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs index c3a3482f91..daab39bb00 100644 --- a/library/std/src/sys/windows/handle.rs +++ b/library/std/src/sys/windows/handle.rs @@ -262,26 +262,17 @@ impl Handle { Ok(written as usize) } + pub fn try_clone(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + pub fn duplicate( &self, access: c::DWORD, inherit: bool, options: c::DWORD, - ) -> io::Result { - let mut ret = 0 as c::HANDLE; - cvt(unsafe { - let cur_proc = c::GetCurrentProcess(); - c::DuplicateHandle( - cur_proc, - self.as_raw_handle(), - cur_proc, - &mut ret, - access, - inherit as c::BOOL, - options, - ) - })?; - unsafe { Ok(Handle::from_raw_handle(ret)) } + ) -> io::Result { + Ok(Self(self.0.duplicate(access, inherit, options)?)) } } diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index 084af4325e..dc28817634 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -71,6 +71,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::ERROR_FILE_NOT_FOUND => return NotFound, c::ERROR_PATH_NOT_FOUND => return NotFound, c::ERROR_NO_DATA => return BrokenPipe, + c::ERROR_INVALID_NAME => return InvalidFilename, c::ERROR_INVALID_PARAMETER => return InvalidInput, c::ERROR_NOT_ENOUGH_MEMORY | c::ERROR_OUTOFMEMORY => return OutOfMemory, c::ERROR_SEM_TIMEOUT @@ -104,7 +105,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, c::ERROR_NOT_SAME_DEVICE => return CrossesDevices, c::ERROR_TOO_MANY_LINKS => return TooManyLinks, - c::ERROR_FILENAME_EXCED_RANGE => return FilenameTooLong, + c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename, _ => {} } @@ -160,9 +161,9 @@ pub fn to_u16s>(s: S) -> crate::io::Result> { fn inner(s: &OsStr) -> crate::io::Result> { let mut maybe_result: Vec = s.encode_wide().collect(); if unrolled_find_u16s(0, &maybe_result).is_some() { - return Err(crate::io::Error::new_const( + return Err(crate::io::const_io_error!( ErrorKind::InvalidInput, - &"strings passed to WinAPI cannot contain NULs", + "strings passed to WinAPI cannot contain NULs", )); } maybe_result.push(0); @@ -285,6 +286,7 @@ pub fn dur2timeout(dur: Duration) -> c::DWORD { #[allow(unreachable_code)] pub fn abort_internal() -> ! { const FAST_FAIL_FATAL_APP_EXIT: usize = 7; + #[cfg(not(miri))] // inline assembly does not work in Miri unsafe { cfg_if::cfg_if! { if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 9c631e7e51..aa6400aeef 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -134,7 +134,7 @@ impl Socket { unsafe { let socket = Self::from_raw_socket(socket); - socket.set_no_inherit()?; + socket.0.set_no_inherit()?; Ok(socket) } } @@ -152,9 +152,9 @@ impl Socket { match result { Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => { if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } @@ -185,9 +185,7 @@ impl Socket { }; match count { - 0 => { - Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out")) - } + 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")), _ => { if writefds.fd_count != 1 { if let Some(e) = self.take_error()? { @@ -213,52 +211,7 @@ impl Socket { } pub fn duplicate(&self) -> io::Result { - let mut info = unsafe { mem::zeroed::() }; - let result = unsafe { - c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info) - }; - cvt(result)?; - let socket = unsafe { - c::WSASocketW( - info.iAddressFamily, - info.iSocketType, - info.iProtocol, - &mut info, - 0, - c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, - ) - }; - - if socket != c::INVALID_SOCKET { - unsafe { Ok(Self::from_inner(OwnedSocket::from_raw_socket(socket))) } - } else { - let error = unsafe { c::WSAGetLastError() }; - - if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL { - return Err(io::Error::from_raw_os_error(error)); - } - - let socket = unsafe { - c::WSASocketW( - info.iAddressFamily, - info.iSocketType, - info.iProtocol, - &mut info, - 0, - c::WSA_FLAG_OVERLAPPED, - ) - }; - - if socket == c::INVALID_SOCKET { - return Err(last_error()); - } - - unsafe { - let socket = Self::from_inner(OwnedSocket::from_raw_socket(socket)); - socket.set_no_inherit()?; - Ok(socket) - } - } + Ok(Self(self.0.try_clone()?)) } fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { @@ -398,9 +351,9 @@ impl Socket { Some(dur) => { let timeout = sys::dur2timeout(dur); if timeout == 0 { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"cannot set a 0 duration timeout", + "cannot set a 0 duration timeout", )); } timeout @@ -421,19 +374,6 @@ impl Socket { } } - #[cfg(not(target_vendor = "uwp"))] - fn set_no_inherit(&self) -> io::Result<()> { - sys::cvt(unsafe { - c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) - }) - .map(drop) - } - - #[cfg(target_vendor = "uwp")] - fn set_no_inherit(&self) -> io::Result<()> { - Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP")) - } - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { let how = match how { Shutdown::Write => c::SD_SEND, diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index 79e0eaf6c3..e54fcaed49 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -260,3 +260,19 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result> { )?; Ok(path) } + +/// Make a Windows path absolute. +pub(crate) fn absolute(path: &Path) -> io::Result { + if path.as_os_str().bytes().starts_with(br"\\?\") { + return Ok(path.into()); + } + let path = to_u16s(path)?; + let lpfilename = path.as_ptr(); + fill_utf16_buf( + // SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid. + // `lpfilename` is a pointer to a null terminated string that is not + // invalidated until after `GetFullPathNameW` returns successfully. + |buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) }, + super::os2path, + ) +} diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index 5ad5704279..fafd1412d4 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -149,7 +149,7 @@ impl AsRef for EnvKey { fn ensure_no_nuls>(str: T) -> io::Result { if str.as_ref().encode_wide().any(|b| b == 0) { - Err(io::Error::new_const(ErrorKind::InvalidInput, &"nul byte found in provided data")) + Err(io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) } else { Ok(str) } @@ -369,9 +369,9 @@ fn resolve_exe<'a>( ) -> io::Result { // Early return if there is no filename. if exe_path.is_empty() || path::has_trailing_slash(exe_path) { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, - &"program path has no file name", + "program path has no file name", )); } // Test if the file name has the `exe` extension. @@ -394,7 +394,7 @@ fn resolve_exe<'a>( // Append `.exe` if not already there. path = path::append_suffix(path, EXE_SUFFIX.as_ref()); - if path.try_exists().unwrap_or(false) { + if program_exists(&path) { return Ok(path); } else { // It's ok to use `set_extension` here because the intent is to @@ -415,14 +415,14 @@ fn resolve_exe<'a>( if !has_extension { path.set_extension(EXE_EXTENSION); } - if let Ok(true) = path.try_exists() { Some(path) } else { None } + if program_exists(&path) { Some(path) } else { None } }); if let Some(path) = result { return Ok(path); } } // If we get here then the executable cannot be found. - Err(io::Error::new_const(io::ErrorKind::NotFound, &"program not found")) + Err(io::const_io_error!(io::ErrorKind::NotFound, "program not found")) } // Calls `f` for every path that should be used to find an executable. @@ -485,6 +485,21 @@ where None } +/// Check if a file exists without following symlinks. +fn program_exists(path: &Path) -> bool { + unsafe { + to_u16s(path) + .map(|path| { + // Getting attributes using `GetFileAttributesW` does not follow symlinks + // and it will almost always be successful if the link exists. + // There are some exceptions for special system files (e.g. the pagefile) + // but these are not executable. + c::GetFileAttributesW(path.as_ptr()) != c::INVALID_FILE_ATTRIBUTES + }) + .unwrap_or(false) + } +} + impl Stdio { fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option) -> io::Result { match *self { @@ -666,6 +681,12 @@ impl ExitCode { } } +impl From for ExitCode { + fn from(code: u8) -> Self { + ExitCode(c::DWORD::from(code)) + } +} + fn zeroed_startupinfo() -> c::STARTUPINFO { c::STARTUPINFO { cb: 0, diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs index f1221767af..d18c3d855b 100644 --- a/library/std/src/sys/windows/process/tests.rs +++ b/library/std/src/sys/windows/process/tests.rs @@ -135,6 +135,8 @@ fn windows_env_unicode_case() { fn windows_exe_resolver() { use super::resolve_exe; use crate::io; + use crate::sys::fs::symlink; + use crate::sys_common::io::test::tmpdir; let env_paths = || env::var_os("PATH"); @@ -178,4 +180,13 @@ fn windows_exe_resolver() { // The application's directory is also searched. let current_exe = env::current_exe().unwrap(); assert!(resolve_exe(current_exe.file_name().unwrap().as_ref(), empty_paths, None).is_ok()); + + // Create a temporary path and add a broken symlink. + let temp = tmpdir(); + let mut exe_path = temp.path().to_owned(); + exe_path.push("exists.exe"); + symlink("".as_ref(), &exe_path).unwrap(); + + // A broken symlink should still be resolved. + assert!(resolve_exe(OsStr::new("exists.exe"), empty_paths, Some(temp.path().as_ref())).is_ok()); } diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 684b8e3155..a001d6b985 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -110,9 +110,9 @@ fn write( if data[0] >> 6 != 0b10 { // not a continuation byte - reject incomplete_utf8.len = 0; - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, - &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); } incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0]; @@ -132,9 +132,9 @@ fn write( return Ok(1); } Err(_) => { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, - &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); } } @@ -156,9 +156,9 @@ fn write( incomplete_utf8.len = 1; return Ok(1); } else { - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, - &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); } } @@ -364,9 +364,9 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { } Err(_) => { // We can't really do any better than forget all data and return an error. - return Err(io::Error::new_const( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, - &"Windows stdin in console mode does not support non-UTF-16 input; \ + "Windows stdin in console mode does not support non-UTF-16 input; \ encountered unpaired surrogate", )); } diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs index 75f70c2076..e4bba9255d 100644 --- a/library/std/src/sys/windows/thread.rs +++ b/library/std/src/sys/windows/thread.rs @@ -107,9 +107,9 @@ pub fn available_parallelism() -> io::Result { sysinfo.dwNumberOfProcessors as usize }; match res { - 0 => Err(io::Error::new_const( + 0 => Err(io::const_io_error!( io::ErrorKind::NotFound, - &"The number of hardware threads is not known for the target platform", + "The number of hardware threads is not known for the target platform", )), cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }), } diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs index 91e4f76548..a04908b541 100644 --- a/library/std/src/sys/windows/time.rs +++ b/library/std/src/sys/windows/time.rs @@ -41,14 +41,6 @@ impl Instant { perf_counter::PerformanceCounterInstant::now().into() } - pub fn actually_monotonic() -> bool { - false - } - - pub const fn zero() -> Instant { - Instant { t: Duration::from_secs(0) } - } - pub fn checked_sub_instant(&self, other: &Instant) -> Option { // On windows there's a threshold below which we consider two timestamps // equivalent due to measurement error. For more details + doc link, diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index d5e8f12414..b0b55592f6 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -7,7 +7,6 @@ use crate::fmt; use crate::io; use crate::io::prelude::*; use crate::path::{self, Path, PathBuf}; -use crate::sync::atomic::{self, Ordering}; use crate::sys_common::mutex::StaticMutex; /// Max number of frames to print. @@ -144,51 +143,6 @@ where result } -pub enum RustBacktrace { - Print(PrintFmt), - Disabled, - RuntimeDisabled, -} - -// For now logging is turned off by default, and this function checks to see -// whether the magical environment variable is present to see if it's turned on. -pub fn rust_backtrace_env() -> RustBacktrace { - // If the `backtrace` feature of this crate isn't enabled quickly return - // `None` so this can be constant propagated all over the place to turn - // optimize away callers. - if !cfg!(feature = "backtrace") { - return RustBacktrace::Disabled; - } - - // Setting environment variables for Fuchsia components isn't a standard - // or easily supported workflow. For now, always display backtraces. - if cfg!(target_os = "fuchsia") { - return RustBacktrace::Print(PrintFmt::Full); - } - - static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0); - match ENABLED.load(Ordering::SeqCst) { - 0 => {} - 1 => return RustBacktrace::RuntimeDisabled, - 2 => return RustBacktrace::Print(PrintFmt::Short), - _ => return RustBacktrace::Print(PrintFmt::Full), - } - - let (format, cache) = env::var_os("RUST_BACKTRACE") - .map(|x| { - if &x == "0" { - (RustBacktrace::RuntimeDisabled, 1) - } else if &x == "full" { - (RustBacktrace::Print(PrintFmt::Full), 3) - } else { - (RustBacktrace::Print(PrintFmt::Short), 2) - } - }) - .unwrap_or((RustBacktrace::RuntimeDisabled, 1)); - ENABLED.store(cache, Ordering::SeqCst); - format -} - /// Prints the filename of the backtrace frame. /// /// See also `output`. diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs index 309f548387..617ac52e51 100644 --- a/library/std/src/sys_common/fs.rs +++ b/library/std/src/sys_common/fs.rs @@ -4,9 +4,9 @@ use crate::fs; use crate::io::{self, Error, ErrorKind}; use crate::path::Path; -pub(crate) const NOT_FILE_ERROR: Error = Error::new_const( +pub(crate) const NOT_FILE_ERROR: Error = io::const_io_error!( ErrorKind::InvalidInput, - &"the source path is neither a regular file nor a symlink to a regular file", + "the source path is neither a regular file nor a symlink to a regular file", ); pub fn copy(from: &Path, to: &Path) -> io::Result { diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs index ea9108f171..d1e9fed41f 100644 --- a/library/std/src/sys_common/io.rs +++ b/library/std/src/sys_common/io.rs @@ -8,6 +8,7 @@ pub mod test { use crate::env; use crate::fs; use crate::path::{Path, PathBuf}; + use crate::thread; use rand::RngCore; pub struct TempDir(PathBuf); @@ -29,7 +30,12 @@ pub mod test { // Gee, seeing how we're testing the fs module I sure hope that we // at least implement this correctly! let TempDir(ref p) = *self; - fs::remove_dir_all(p).unwrap(); + let result = fs::remove_dir_all(p); + // Avoid panicking while panicking as this causes the process to + // immediately abort, without displaying test results. + if !thread::panicking() { + result.unwrap(); + } } } diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index c5c3df361f..70b29d4a92 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -5,7 +5,7 @@ use crate::cmp; use crate::convert::{TryFrom, TryInto}; use crate::ffi::CString; use crate::fmt; -use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut}; +use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::ptr; @@ -102,7 +102,7 @@ pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result *(storage as *const _ as *const c::sockaddr_in6) }))) } - _ => Err(Error::new_const(ErrorKind::InvalidInput, &"invalid argument")), + _ => Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid argument")), } } @@ -165,7 +165,7 @@ impl TryFrom<&str> for LookupHost { ($e:expr, $msg:expr) => { match $e { Some(r) => r, - None => return Err(io::Error::new_const(io::ErrorKind::InvalidInput, &$msg)), + None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, $msg)), } }; } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 1d2f6e9768..1be3ed757b 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -142,6 +142,7 @@ impl fmt::Debug for LocalKey { /// [`std::thread::LocalKey`]: crate::thread::LocalKey #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")] #[allow_internal_unstable(thread_local_internals)] macro_rules! thread_local { // empty (base case for the recursion) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index ae4b65871e..f8d790c378 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -180,6 +180,12 @@ use crate::time::Duration; #[macro_use] mod local; +#[unstable(feature = "scoped_threads", issue = "93203")] +mod scoped; + +#[unstable(feature = "scoped_threads", issue = "93203")] +pub use scoped::{scope, Scope, ScopedJoinHandle}; + #[stable(feature = "rust1", since = "1.0.0")] pub use self::local::{AccessError, LocalKey}; @@ -446,6 +452,20 @@ impl Builder { F: FnOnce() -> T, F: Send + 'a, T: Send + 'a, + { + Ok(JoinHandle(unsafe { self.spawn_unchecked_(f, None) }?)) + } + + unsafe fn spawn_unchecked_<'a, 'scope, F, T>( + self, + f: F, + scope_data: Option<&'scope scoped::ScopeData>, + ) -> io::Result> + where + F: FnOnce() -> T, + F: Send + 'a, + T: Send + 'a, + 'scope: 'a, { let Builder { name, stack_size } = self; @@ -456,7 +476,8 @@ impl Builder { })); let their_thread = my_thread.clone(); - let my_packet: Arc>>> = Arc::new(UnsafeCell::new(None)); + let my_packet: Arc> = + Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None) }); let their_packet = my_packet.clone(); let output_capture = crate::io::set_output_capture(None); @@ -480,10 +501,14 @@ impl Builder { // closure (it is an Arc<...>) and `my_packet` will be stored in the // same `JoinInner` as this closure meaning the mutation will be // safe (not modify it and affect a value far away). - unsafe { *their_packet.get() = Some(try_result) }; + unsafe { *their_packet.result.get() = Some(try_result) }; }; - Ok(JoinHandle(JoinInner { + if let Some(scope_data) = scope_data { + scope_data.increment_num_running_threads(); + } + + Ok(JoinInner { // SAFETY: // // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed @@ -498,16 +523,16 @@ impl Builder { // exist after the thread has terminated, which is signaled by `Thread::join` // returning. native: unsafe { - Some(imp::Thread::new( + imp::Thread::new( stack_size, mem::transmute::, Box>( Box::new(main), ), - )?) + )? }, thread: my_thread, - packet: Packet(my_packet), - })) + packet: my_packet, + }) } } @@ -1242,34 +1267,48 @@ impl fmt::Debug for Thread { #[stable(feature = "rust1", since = "1.0.0")] pub type Result = crate::result::Result>; -// This packet is used to communicate the return value between the spawned thread -// and the rest of the program. Memory is shared through the `Arc` within and there's -// no need for a mutex here because synchronization happens with `join()` (the -// caller will never read this packet until the thread has exited). +// This packet is used to communicate the return value between the spawned +// thread and the rest of the program. It is shared through an `Arc` and +// there's no need for a mutex here because synchronization happens with `join()` +// (the caller will never read this packet until the thread has exited). // -// This packet itself is then stored into a `JoinInner` which in turns is placed -// in `JoinHandle` and `JoinGuard`. Due to the usage of `UnsafeCell` we need to -// manually worry about impls like Send and Sync. The type `T` should -// already always be Send (otherwise the thread could not have been created) and -// this type is inherently Sync because no methods take &self. Regardless, -// however, we add inheriting impls for Send/Sync to this type to ensure it's -// Send/Sync and that future modifications will still appropriately classify it. -struct Packet(Arc>>>); - -unsafe impl Send for Packet {} -unsafe impl Sync for Packet {} +// An Arc to the packet is stored into a `JoinInner` which in turns is placed +// in `JoinHandle`. +struct Packet<'scope, T> { + scope: Option<&'scope scoped::ScopeData>, + result: UnsafeCell>>, +} + +// Due to the usage of `UnsafeCell` we need to manually implement Sync. +// The type `T` should already always be Send (otherwise the thread could not +// have been created) and the Packet is Sync because all access to the +// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. +unsafe impl<'scope, T: Sync> Sync for Packet<'scope, T> {} + +impl<'scope, T> Drop for Packet<'scope, T> { + fn drop(&mut self) { + // Book-keeping so the scope knows when it's done. + if let Some(scope) = self.scope { + // If this packet was for a thread that ran in a scope, the thread + // panicked, and nobody consumed the panic payload, we make sure + // the scope function will panic. + let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); + scope.decrement_num_running_threads(unhandled_panic); + } + } +} /// Inner representation for JoinHandle -struct JoinInner { - native: Option, +struct JoinInner<'scope, T> { + native: imp::Thread, thread: Thread, - packet: Packet, + packet: Arc>, } -impl JoinInner { - fn join(&mut self) -> Result { - self.native.take().unwrap().join(); - unsafe { (*self.packet.0.get()).take().unwrap() } +impl<'scope, T> JoinInner<'scope, T> { + fn join(mut self) -> Result { + self.native.join(); + Arc::get_mut(&mut self.packet).unwrap().result.get_mut().take().unwrap() } } @@ -1336,7 +1375,7 @@ impl JoinInner { /// [`thread::Builder::spawn`]: Builder::spawn /// [`thread::spawn`]: spawn #[stable(feature = "rust1", since = "1.0.0")] -pub struct JoinHandle(JoinInner); +pub struct JoinHandle(JoinInner<'static, T>); #[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] unsafe impl Send for JoinHandle {} @@ -1400,29 +1439,29 @@ impl JoinHandle { /// join_handle.join().expect("Couldn't join on the associated thread"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn join(mut self) -> Result { + pub fn join(self) -> Result { self.0.join() } - /// Checks if the the associated thread is still running its main function. + /// Checks if the associated thread is still running its main function. /// /// This might return `false` for a brief moment after the thread's main /// function has returned, but before the thread itself has stopped running. #[unstable(feature = "thread_is_running", issue = "90470")] pub fn is_running(&self) -> bool { - Arc::strong_count(&self.0.packet.0) > 1 + Arc::strong_count(&self.0.packet) > 1 } } impl AsInner for JoinHandle { fn as_inner(&self) -> &imp::Thread { - self.0.native.as_ref().unwrap() + &self.0.native } } impl IntoInner for JoinHandle { fn into_inner(self) -> imp::Thread { - self.0.native.unwrap() + self.0.native } } diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs new file mode 100644 index 0000000000..9dd7c15fc5 --- /dev/null +++ b/library/std/src/thread/scoped.rs @@ -0,0 +1,316 @@ +use super::{current, park, Builder, JoinInner, Result, Thread}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; +use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::sync::Arc; + +/// A scope to spawn scoped threads in. +/// +/// See [`scope`] for details. +pub struct Scope<'env> { + data: ScopeData, + /// Invariance over 'env, to make sure 'env cannot shrink, + /// which is necessary for soundness. + /// + /// Without invariance, this would compile fine but be unsound: + /// + /// ```compile_fail + /// #![feature(scoped_threads)] + /// + /// std::thread::scope(|s| { + /// s.spawn(|s| { + /// let a = String::from("abcd"); + /// s.spawn(|_| println!("{:?}", a)); // might run after `a` is dropped + /// }); + /// }); + /// ``` + env: PhantomData<&'env mut &'env ()>, +} + +/// An owned permission to join on a scoped thread (block on its termination). +/// +/// See [`Scope::spawn`] for details. +pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>); + +pub(super) struct ScopeData { + num_running_threads: AtomicUsize, + a_thread_panicked: AtomicBool, + main_thread: Thread, +} + +impl ScopeData { + pub(super) fn increment_num_running_threads(&self) { + // We check for 'overflow' with usize::MAX / 2, to make sure there's no + // chance it overflows to 0, which would result in unsoundness. + if self.num_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 { + // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles. + self.decrement_num_running_threads(false); + panic!("too many running threads in thread scope"); + } + } + pub(super) fn decrement_num_running_threads(&self, panic: bool) { + if panic { + self.a_thread_panicked.store(true, Ordering::Relaxed); + } + if self.num_running_threads.fetch_sub(1, Ordering::Release) == 1 { + self.main_thread.unpark(); + } + } +} + +/// Create a scope for spawning scoped threads. +/// +/// The function passed to `scope` will be provided a [`Scope`] object, +/// through which scoped threads can be [spawned][`Scope::spawn`]. +/// +/// Unlike non-scoped threads, scoped threads can borrow non-`'static` data, +/// as the scope guarantees all threads will be joined at the end of the scope. +/// +/// All threads spawned within the scope that haven't been manually joined +/// will be automatically joined before this function returns. +/// +/// # Panics +/// +/// If any of the automatically joined threads panicked, this function will panic. +/// +/// If you want to handle panics from spawned threads, +/// [`join`][ScopedJoinHandle::join] them before the end of the scope. +/// +/// # Example +/// +/// ``` +/// #![feature(scoped_threads)] +/// use std::thread; +/// +/// let mut a = vec![1, 2, 3]; +/// let mut x = 0; +/// +/// thread::scope(|s| { +/// s.spawn(|_| { +/// println!("hello from the first scoped thread"); +/// // We can borrow `a` here. +/// dbg!(&a); +/// }); +/// s.spawn(|_| { +/// println!("hello from the second scoped thread"); +/// // We can even mutably borrow `x` here, +/// // because no other threads are using it. +/// x += a[0] + a[2]; +/// }); +/// println!("hello from the main thread"); +/// }); +/// +/// // After the scope, we can modify and access our variables again: +/// a.push(4); +/// assert_eq!(x, a.len()); +/// ``` +#[track_caller] +pub fn scope<'env, F, T>(f: F) -> T +where + F: FnOnce(&Scope<'env>) -> T, +{ + let scope = Scope { + data: ScopeData { + num_running_threads: AtomicUsize::new(0), + main_thread: current(), + a_thread_panicked: AtomicBool::new(false), + }, + env: PhantomData, + }; + + // Run `f`, but catch panics so we can make sure to wait for all the threads to join. + let result = catch_unwind(AssertUnwindSafe(|| f(&scope))); + + // Wait until all the threads are finished. + while scope.data.num_running_threads.load(Ordering::Acquire) != 0 { + park(); + } + + // Throw any panic from `f`, or the return value of `f` if no thread panicked. + match result { + Err(e) => resume_unwind(e), + Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => { + panic!("a scoped thread panicked") + } + Ok(result) => result, + } +} + +impl<'env> Scope<'env> { + /// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it. + /// + /// Unlike non-scoped threads, threads spawned with this function may + /// borrow non-`'static` data from the outside the scope. See [`scope`] for + /// details. + /// + /// The join handle provides a [`join`] method that can be used to join the spawned + /// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing + /// the panic payload. + /// + /// If the join handle is dropped, the spawned thread will implicitly joined at the + /// end of the scope. In that case, if the spawned thread panics, [`scope`] will + /// panic after all threads are joined. + /// + /// This call will create a thread using default parameters of [`Builder`]. + /// If you want to specify the stack size or the name of the thread, use + /// [`Builder::spawn_scoped`] instead. + /// + /// # Panics + /// + /// Panics if the OS fails to create a thread; use [`Builder::spawn_scoped`] + /// to recover from such errors. + /// + /// [`join`]: ScopedJoinHandle::join + pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T> + where + F: FnOnce(&Scope<'env>) -> T + Send + 'env, + T: Send + 'env, + { + Builder::new().spawn_scoped(self, f).expect("failed to spawn thread") + } +} + +impl Builder { + /// Spawns a new scoped thread using the settings set through this `Builder`. + /// + /// Unlike [`Scope::spawn`], this method yields an [`io::Result`] to + /// capture any failure to create the thread at the OS level. + /// + /// [`io::Result`]: crate::io::Result + /// + /// # Panics + /// + /// Panics if a thread name was set and it contained null bytes. + /// + /// # Example + /// + /// ``` + /// #![feature(scoped_threads)] + /// use std::thread; + /// + /// let mut a = vec![1, 2, 3]; + /// let mut x = 0; + /// + /// thread::scope(|s| { + /// thread::Builder::new() + /// .name("first".to_string()) + /// .spawn_scoped(s, |_| + /// { + /// println!("hello from the {:?} scoped thread", thread::current().name()); + /// // We can borrow `a` here. + /// dbg!(&a); + /// }) + /// .unwrap(); + /// thread::Builder::new() + /// .name("second".to_string()) + /// .spawn_scoped(s, |_| + /// { + /// println!("hello from the {:?} scoped thread", thread::current().name()); + /// // We can even mutably borrow `x` here, + /// // because no other threads are using it. + /// x += a[0] + a[2]; + /// }) + /// .unwrap(); + /// println!("hello from the main thread"); + /// }); + /// + /// // After the scope, we can modify and access our variables again: + /// a.push(4); + /// assert_eq!(x, a.len()); + /// ``` + pub fn spawn_scoped<'scope, 'env, F, T>( + self, + scope: &'scope Scope<'env>, + f: F, + ) -> io::Result> + where + F: FnOnce(&Scope<'env>) -> T + Send + 'env, + T: Send + 'env, + { + Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(|| f(scope), Some(&scope.data)) }?)) + } +} + +impl<'scope, T> ScopedJoinHandle<'scope, T> { + /// Extracts a handle to the underlying thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(scoped_threads)] + /// #![feature(thread_is_running)] + /// + /// use std::thread; + /// + /// thread::scope(|s| { + /// let t = s.spawn(|_| { + /// println!("hello"); + /// }); + /// println!("thread id: {:?}", t.thread().id()); + /// }); + /// ``` + #[must_use] + pub fn thread(&self) -> &Thread { + &self.0.thread + } + + /// Waits for the associated thread to finish. + /// + /// This function will return immediately if the associated thread has already finished. + /// + /// In terms of [atomic memory orderings], the completion of the associated + /// thread synchronizes with this function returning. + /// In other words, all operations performed by that thread + /// [happen before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) + /// all operations that happen after `join` returns. + /// + /// If the associated thread panics, [`Err`] is returned with the panic payload. + /// + /// [atomic memory orderings]: crate::sync::atomic + /// + /// # Examples + /// + /// ``` + /// #![feature(scoped_threads)] + /// #![feature(thread_is_running)] + /// + /// use std::thread; + /// + /// thread::scope(|s| { + /// let t = s.spawn(|_| { + /// panic!("oh no"); + /// }); + /// assert!(t.join().is_err()); + /// }); + /// ``` + pub fn join(self) -> Result { + self.0.join() + } + + /// Checks if the associated thread is still running its main function. + /// + /// This might return `false` for a brief moment after the thread's main + /// function has returned, but before the thread itself has stopped running. + #[unstable(feature = "thread_is_running", issue = "90470")] + pub fn is_running(&self) -> bool { + Arc::strong_count(&self.0.packet) > 1 + } +} + +impl<'env> fmt::Debug for Scope<'env> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Scope") + .field("num_running_threads", &self.data.num_running_threads.load(Ordering::Relaxed)) + .field("a_thread_panicked", &self.data.a_thread_panicked.load(Ordering::Relaxed)) + .field("main_thread", &self.data.main_thread) + .finish_non_exhaustive() + } +} + +impl<'scope, T> fmt::Debug for ScopedJoinHandle<'scope, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ScopedJoinHandle").finish_non_exhaustive() + } +} diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 86cc93c445..df8a726e64 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -31,7 +31,6 @@ #![stable(feature = "time", since = "1.3.0")] -mod monotonic; #[cfg(test)] mod tests; @@ -45,16 +44,16 @@ use crate::sys_common::FromInner; pub use core::time::Duration; #[unstable(feature = "duration_checked_float", issue = "83400")] -pub use core::time::FromSecsError; +pub use core::time::FromFloatSecsError; /// A measurement of a monotonically nondecreasing clock. /// Opaque and useful only with [`Duration`]. /// -/// Instants are always guaranteed to be no less than any previously measured -/// instant when created, and are often useful for tasks such as measuring +/// Instants are always guaranteed, barring [platform bugs], to be no less than any previously +/// measured instant when created, and are often useful for tasks such as measuring /// benchmarks or timing how long an operation takes. /// -/// Note, however, that instants are not guaranteed to be **steady**. In other +/// Note, however, that instants are **not** guaranteed to be **steady**. In other /// words, each tick of the underlying clock might not be the same length (e.g. /// some seconds may be longer than others). An instant may jump forwards or /// experience time dilation (slow down or speed up), but it will never go @@ -84,6 +83,8 @@ pub use core::time::FromSecsError; /// } /// ``` /// +/// [platform bugs]: Instant#monotonicity +/// /// # OS-specific behaviors /// /// An `Instant` is a wrapper around system-specific types and it may behave @@ -125,6 +126,26 @@ pub use core::time::FromSecsError; /// > structure cannot represent the new point in time. /// /// [`add`]: Instant::add +/// +/// ## Monotonicity +/// +/// On all platforms `Instant` will try to use an OS API that guarantees monotonic behavior +/// if available, which is the case for all [tier 1] platforms. +/// In practice such guarantees are – under rare circumstances – broken by hardware, virtualization +/// or operating system bugs. To work around these bugs and platforms not offering monotonic clocks +/// [`duration_since`], [`elapsed`] and [`sub`] saturate to zero. In older Rust versions this +/// lead to a panic instead. [`checked_duration_since`] can be used to detect and handle situations +/// where monotonicity is violated, or `Instant`s are subtracted in the wrong order. +/// +/// This workaround obscures programming errors where earlier and later instants are accidentally +/// swapped. For this reason future rust versions may reintroduce panics. +/// +/// [tier 1]: https://doc.rust-lang.org/rustc/platform-support.html +/// [`duration_since`]: Instant::duration_since +/// [`elapsed`]: Instant::elapsed +/// [`sub`]: Instant::sub +/// [`checked_duration_since`]: Instant::checked_duration_since +/// #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[stable(feature = "time2", since = "1.8.0")] pub struct Instant(time::Instant); @@ -176,7 +197,12 @@ pub struct Instant(time::Instant); /// } /// ``` /// -/// # Underlying System calls +/// # Platform-specific behavior +/// +/// The precision of `SystemTime` can depend on the underlying OS-specific time format. +/// For example, on Windows the time is represented in 100 nanosecond intervals whereas Linux +/// can represent nanosecond intervals. +/// /// Currently, the following system calls are being used to get the current time using `now()`: /// /// | Platform | System call | @@ -242,59 +268,19 @@ impl Instant { #[must_use] #[stable(feature = "time2", since = "1.8.0")] pub fn now() -> Instant { - let os_now = time::Instant::now(); - - // And here we come upon a sad state of affairs. The whole point of - // `Instant` is that it's monotonically increasing. We've found in the - // wild, however, that it's not actually monotonically increasing for - // one reason or another. These appear to be OS and hardware level bugs, - // and there's not really a whole lot we can do about them. Here's a - // taste of what we've found: - // - // * #48514 - OpenBSD, x86_64 - // * #49281 - linux arm64 and s390x - // * #51648 - windows, x86 - // * #56560 - windows, x86_64, AWS - // * #56612 - windows, x86, vm (?) - // * #56940 - linux, arm64 - // * https://bugzilla.mozilla.org/show_bug.cgi?id=1487778 - a similar - // Firefox bug - // - // It seems that this just happens a lot in the wild. - // We're seeing panics across various platforms where consecutive calls - // to `Instant::now`, such as via the `elapsed` function, are panicking - // as they're going backwards. Placed here is a last-ditch effort to try - // to fix things up. We keep a global "latest now" instance which is - // returned instead of what the OS says if the OS goes backwards. - // - // To hopefully mitigate the impact of this, a few platforms are - // excluded as "these at least haven't gone backwards yet". - // - // While issues have been seen on arm64 platforms the Arm architecture - // requires that the counter monotonically increases and that it must - // provide a uniform view of system time (e.g. it must not be possible - // for a core to receive a message from another core with a time stamp - // and observe time going backwards (ARM DDI 0487G.b D11.1.2). While - // there have been a few 64bit SoCs that have bugs which cause time to - // not monoticially increase, these have been fixed in the Linux kernel - // and we shouldn't penalize all Arm SoCs for those who refuse to - // update their kernels: - // SUN50I_ERRATUM_UNKNOWN1 - Allwinner A64 / Pine A64 - fixed in 5.1 - // FSL_ERRATUM_A008585 - Freescale LS2080A/LS1043A - fixed in 4.10 - // HISILICON_ERRATUM_161010101 - Hisilicon 1610 - fixed in 4.11 - // ARM64_ERRATUM_858921 - Cortex A73 - fixed in 4.12 - if time::Instant::actually_monotonic() { - return Instant(os_now); - } - - Instant(monotonic::monotonize(os_now)) + Instant(time::Instant::now()) } - /// Returns the amount of time elapsed from another instant to this one. + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. /// /// # Panics /// - /// This function will panic if `earlier` is later than `self`. + /// Previous rust versions panicked when `earlier` was later than `self`. Currently this + /// method saturates. Future versions may reintroduce the panic in some circumstances. + /// See [Monotonicity]. + /// + /// [Monotonicity]: Instant#monotonicity /// /// # Examples /// @@ -306,16 +292,22 @@ impl Instant { /// sleep(Duration::new(1, 0)); /// let new_now = Instant::now(); /// println!("{:?}", new_now.duration_since(now)); + /// println!("{:?}", now.duration_since(new_now)); // 0ns /// ``` #[must_use] #[stable(feature = "time2", since = "1.8.0")] pub fn duration_since(&self, earlier: Instant) -> Duration { - self.0.checked_sub_instant(&earlier.0).expect("supplied instant is later than self") + self.checked_duration_since(earlier).unwrap_or_default() } /// Returns the amount of time elapsed from another instant to this one, /// or None if that instant is later than this one. /// + /// Due to [monotonicity bugs], even under correct logical ordering of the passed `Instant`s, + /// this method can return `None`. + /// + /// [monotonicity bugs]: Instant#monotonicity + /// /// # Examples /// /// ```no_run @@ -359,9 +351,11 @@ impl Instant { /// /// # Panics /// - /// This function may panic if the current time is earlier than this - /// instant, which is something that can happen if an `Instant` is - /// produced synthetically. + /// Previous rust versions panicked when self was earlier than the current time. Currently this + /// method returns a Duration of zero in that case. Future versions may reintroduce the panic. + /// See [Monotonicity]. + /// + /// [Monotonicity]: Instant#monotonicity /// /// # Examples /// @@ -437,6 +431,16 @@ impl SubAssign for Instant { impl Sub for Instant { type Output = Duration; + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + /// + /// # Panics + /// + /// Previous rust versions panicked when `other` was later than `self`. Currently this + /// method saturates. Future versions may reintroduce the panic in some circumstances. + /// See [Monotonicity]. + /// + /// [Monotonicity]: Instant#monotonicity fn sub(self, other: Instant) -> Duration { self.duration_since(other) } diff --git a/library/std/src/time/monotonic.rs b/library/std/src/time/monotonic.rs deleted file mode 100644 index 64f16245c2..0000000000 --- a/library/std/src/time/monotonic.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::sys::time; - -#[inline] -pub(super) fn monotonize(raw: time::Instant) -> time::Instant { - inner::monotonize(raw) -} - -#[cfg(any(all(target_has_atomic = "64", not(target_has_atomic = "128")), target_arch = "aarch64"))] -pub mod inner { - use crate::sync::atomic::AtomicU64; - use crate::sync::atomic::Ordering::*; - use crate::sys::time; - use crate::time::Duration; - - pub(in crate::time) const ZERO: time::Instant = time::Instant::zero(); - - // bits 30 and 31 are never used since the nanoseconds part never exceeds 10^9 - const UNINITIALIZED: u64 = 0b11 << 30; - static MONO: AtomicU64 = AtomicU64::new(UNINITIALIZED); - - #[inline] - pub(super) fn monotonize(raw: time::Instant) -> time::Instant { - monotonize_impl(&MONO, raw) - } - - #[inline] - pub(in crate::time) fn monotonize_impl(mono: &AtomicU64, raw: time::Instant) -> time::Instant { - let delta = raw.checked_sub_instant(&ZERO).unwrap(); - let secs = delta.as_secs(); - // occupies no more than 30 bits (10^9 seconds) - let nanos = delta.subsec_nanos() as u64; - - // This wraps around every 136 years (2^32 seconds). - // To detect backsliding we use wrapping arithmetic and declare forward steps smaller - // than 2^31 seconds as expected and everything else as a backslide which will be - // monotonized. - // This could be a problem for programs that call instants at intervals greater - // than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true. - let packed = (secs << 32) | nanos; - let updated = mono.fetch_update(Relaxed, Relaxed, |old| { - (old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2).then_some(packed) - }); - match updated { - Ok(_) => raw, - Err(newer) => { - // Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the - // passed in value and the 64bits loaded from the atomic - let seconds_lower = newer >> 32; - let mut seconds_upper = secs & 0xffff_ffff_0000_0000; - if secs & 0xffff_ffff > seconds_lower { - // Backslide caused the lower 32bit of the seconds part to wrap. - // This must be the case because the seconds part is larger even though - // we are in the backslide branch, i.e. the seconds count should be smaller or equal. - // - // We assume that backslides are smaller than 2^32 seconds - // which means we need to add 1 to the upper half to restore it. - // - // Example: - // most recent observed time: 0xA1_0000_0000_0000_0000u128 - // bits stored in AtomicU64: 0x0000_0000_0000_0000u64 - // backslide by 1s - // caller time is 0xA0_ffff_ffff_0000_0000u128 - // -> we can fix up the upper half time by adding 1 << 32 - seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000); - } - let secs = seconds_upper | seconds_lower; - let nanos = newer as u32; - ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap() - } - } - } -} - -#[cfg(all(target_has_atomic = "128", not(target_arch = "aarch64")))] -pub mod inner { - use crate::sync::atomic::AtomicU128; - use crate::sync::atomic::Ordering::*; - use crate::sys::time; - use crate::time::Duration; - - const ZERO: time::Instant = time::Instant::zero(); - static MONO: AtomicU128 = AtomicU128::new(0); - - #[inline] - pub(super) fn monotonize(raw: time::Instant) -> time::Instant { - let delta = raw.checked_sub_instant(&ZERO).unwrap(); - // Split into seconds and nanos since Duration doesn't have a - // constructor that takes a u128 - let secs = delta.as_secs() as u128; - let nanos = delta.subsec_nanos() as u128; - let timestamp: u128 = secs << 64 | nanos; - let timestamp = MONO.fetch_max(timestamp, Relaxed).max(timestamp); - let secs = (timestamp >> 64) as u64; - let nanos = timestamp as u32; - ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap() - } -} - -#[cfg(not(any(target_has_atomic = "64", target_has_atomic = "128")))] -pub mod inner { - use crate::cmp; - use crate::sys::time; - use crate::sys_common::mutex::StaticMutex; - - #[inline] - pub(super) fn monotonize(os_now: time::Instant) -> time::Instant { - static LOCK: StaticMutex = StaticMutex::new(); - static mut LAST_NOW: time::Instant = time::Instant::zero(); - unsafe { - let _lock = LOCK.lock(); - let now = cmp::max(LAST_NOW, os_now); - LAST_NOW = now; - now - } - } -} diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs index 7279925a6d..d1a69ff869 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/src/time/tests.rs @@ -90,10 +90,9 @@ fn instant_math_is_associative() { } #[test] -#[should_panic] -fn instant_duration_since_panic() { +fn instant_duration_since_saturates() { let a = Instant::now(); - let _ = (a - Duration::SECOND).duration_since(a); + assert_eq!((a - Duration::SECOND).duration_since(a), Duration::ZERO); } #[test] @@ -109,6 +108,7 @@ fn instant_checked_duration_since_nopanic() { #[test] fn instant_saturating_duration_since_nopanic() { let a = Instant::now(); + #[allow(deprecated, deprecated_in_future)] let ret = (a - Duration::SECOND).saturating_duration_since(a); assert_eq!(ret, Duration::ZERO); } @@ -192,31 +192,6 @@ fn since_epoch() { assert!(a < hundred_twenty_years); } -#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))] -#[test] -fn monotonizer_wrapping_backslide() { - use super::monotonic::inner::{monotonize_impl, ZERO}; - use core::sync::atomic::AtomicU64; - - let reference = AtomicU64::new(0); - - let time = match ZERO.checked_add_duration(&Duration::from_secs(0xffff_ffff)) { - Some(time) => time, - None => { - // platform cannot represent u32::MAX seconds so it won't have to deal with this kind - // of overflow either - return; - } - }; - - let monotonized = monotonize_impl(&reference, time); - let expected = ZERO.checked_add_duration(&Duration::from_secs(1 << 32)).unwrap(); - assert_eq!( - monotonized, expected, - "64bit monotonizer should handle overflows in the seconds part" - ); -} - macro_rules! bench_instant_threaded { ($bench_name:ident, $thread_count:expr) => { #[bench] diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs index 079f00a575..54873f5549 100644 --- a/library/std/tests/run-time-detect.rs +++ b/library/std/tests/run-time-detect.rs @@ -3,10 +3,9 @@ #![cfg_attr( any( all(target_arch = "arm", any(target_os = "linux", target_os = "android")), - all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")), + all(bootstrap, target_arch = "aarch64", any(target_os = "linux", target_os = "android")), all(target_arch = "powerpc", target_os = "linux"), all(target_arch = "powerpc64", target_os = "linux"), - any(target_arch = "x86", target_arch = "x86_64"), ), feature(stdsimd) )] @@ -14,6 +13,7 @@ #[test] #[cfg(all(target_arch = "arm", any(target_os = "linux", target_os = "android")))] fn arm_linux() { + use std::arch::is_arm_feature_detected; println!("neon: {}", is_arm_feature_detected!("neon")); println!("pmull: {}", is_arm_feature_detected!("pmull")); println!("crypto: {}", is_arm_feature_detected!("crypto")); @@ -25,6 +25,7 @@ fn arm_linux() { #[test] #[cfg(all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")))] fn aarch64_linux() { + use std::arch::is_aarch64_feature_detected; println!("neon: {}", is_aarch64_feature_detected!("neon")); println!("asimd: {}", is_aarch64_feature_detected!("asimd")); println!("pmull: {}", is_aarch64_feature_detected!("pmull")); @@ -44,7 +45,8 @@ fn aarch64_linux() { println!("flagm: {}", is_aarch64_feature_detected!("flagm")); println!("ssbs: {}", is_aarch64_feature_detected!("ssbs")); println!("sb: {}", is_aarch64_feature_detected!("sb")); - println!("pauth: {}", is_aarch64_feature_detected!("pauth")); + println!("paca: {}", is_aarch64_feature_detected!("paca")); + println!("pacg: {}", is_aarch64_feature_detected!("pacg")); println!("dpb: {}", is_aarch64_feature_detected!("dpb")); println!("dpb2: {}", is_aarch64_feature_detected!("dpb2")); println!("sve2: {}", is_aarch64_feature_detected!("sve2")); @@ -71,6 +73,7 @@ fn aarch64_linux() { #[test] #[cfg(all(target_arch = "powerpc", target_os = "linux"))] fn powerpc_linux() { + use std::arch::is_powerpc_feature_detected; println!("altivec: {}", is_powerpc_feature_detected!("altivec")); println!("vsx: {}", is_powerpc_feature_detected!("vsx")); println!("power8: {}", is_powerpc_feature_detected!("power8")); @@ -79,6 +82,7 @@ fn powerpc_linux() { #[test] #[cfg(all(target_arch = "powerpc64", target_os = "linux"))] fn powerpc64_linux() { + use std::arch::is_powerpc64_feature_detected; println!("altivec: {}", is_powerpc64_feature_detected!("altivec")); println!("vsx: {}", is_powerpc64_feature_detected!("vsx")); println!("power8: {}", is_powerpc64_feature_detected!("power8")); @@ -87,6 +91,8 @@ fn powerpc64_linux() { #[test] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn x86_all() { + use std::arch::is_x86_feature_detected; + // the below is the set of features we can test at runtime, but don't actually // use to gate anything and are thus not part of the X86_ALLOWED_FEATURES list diff --git a/library/stdarch/crates/core_arch/avx512bw.md b/library/stdarch/crates/core_arch/avx512bw.md index fa50c66e6c..20c8c2f14a 100644 --- a/library/stdarch/crates/core_arch/avx512bw.md +++ b/library/stdarch/crates/core_arch/avx512bw.md @@ -1,34 +1,34 @@

["AVX512BW"]

* [x] [`_mm512_loadu_epi16`] - * [_] [`_mm512_mask_loadu_epi16`] //need i1 - * [_] [`_mm512_maskz_loadu_epi16`] //need i1 + * [x] [`_mm512_mask_loadu_epi16`] //need i1 + * [x] [`_mm512_maskz_loadu_epi16`] //need i1 * [x] [`_mm_loadu_epi16`] - * [_] [`_mm_mask_loadu_epi16`] //need i1 - * [_] [`_mm_maskz_loadu_epi16`] //need i1 + * [x] [`_mm_mask_loadu_epi16`] //need i1 + * [x] [`_mm_maskz_loadu_epi16`] //need i1 * [x] [`_mm256_loadu_epi16`] - * [_] [`_mm256_mask_loadu_epi16`] //need i1 - * [_] [`_mm256_maskz_loadu_epi16`] //need i1 + * [x] [`_mm256_mask_loadu_epi16`] //need i1 + * [x] [`_mm256_maskz_loadu_epi16`] //need i1 * [x] [`_mm512_loadu_epi8`] - * [_] [`_mm512_mask_loadu_epi8`] //need i1 - * [_] [`_mm512_maskz_loadu_epi8`] //need i1 + * [x] [`_mm512_mask_loadu_epi8`] //need i1 + * [x] [`_mm512_maskz_loadu_epi8`] //need i1 * [x] [`_mm_loadu_epi8`] - * [_] [`_mm_mask_loadu_epi8`] //need i1 - * [_] [`_mm_maskz_loadu_epi8`] //need i1 + * [x] [`_mm_mask_loadu_epi8`] //need i1 + * [x] [`_mm_maskz_loadu_epi8`] //need i1 * [x] [`_mm256_loadu_epi8`] - * [_] [`_mm256_mask_loadu_epi8`] //need i1 - * [_] [`_mm256_maskz_loadu_epi8`] //need i1 - * [_] [`_mm512_mask_storeu_epi16`] + * [x] [`_mm256_mask_loadu_epi8`] //need i1 + * [x] [`_mm256_maskz_loadu_epi8`] //need i1 + * [x] [`_mm512_mask_storeu_epi16`] * [x] [`_mm512_storeu_epi16`] - * [_] [`_mm_mask_storeu_epi16`] //need i1 + * [x] [`_mm_mask_storeu_epi16`] //need i1 * [x] [`_mm_storeu_epi16`] - * [_] [`_mm256_mask_storeu_epi16`] //need i1 + * [x] [`_mm256_mask_storeu_epi16`] //need i1 * [x] [`_mm256_storeu_epi16`] - * [_] [`_mm512_mask_storeu_epi8`] //need i1 + * [x] [`_mm512_mask_storeu_epi8`] //need i1 * [x] [`_mm512_storeu_epi8`] - * [_] [`_mm_mask_storeu_epi8`] //need i1 + * [x] [`_mm_mask_storeu_epi8`] //need i1 * [x] [`_mm_storeu_epi8`] - * [_] [`_mm256_mask_storeu_epi8`] //need i1 + * [x] [`_mm256_mask_storeu_epi8`] //need i1 * [x] [`_mm256_storeu_epi8`] * [x] [`_mm512_abs_epi16`] * [x] [`_mm512_mask_abs_epi16`] diff --git a/library/stdarch/crates/core_arch/avx512f.md b/library/stdarch/crates/core_arch/avx512f.md index 9d95f0c492..6cb6e65640 100644 --- a/library/stdarch/crates/core_arch/avx512f.md +++ b/library/stdarch/crates/core_arch/avx512f.md @@ -1629,18 +1629,18 @@ * [x] [`_mm_maskz_compress_pd`] * [x] [`_mm256_mask_compress_pd`] * [x] [`_mm256_maskz_compress_pd`] - * [ ] [`_mm512_mask_compressstoreu_epi32`] //need i1 - * [_] [`_mm_mask_compressstoreu_epi32`] //need i1 - * [_] [`_mm256_mask_compressstoreu_epi32`] //need i1 - * [ ] [`_mm512_mask_compressstoreu_epi64`] //need i1 - * [_] [`_mm_mask_compressstoreu_epi64`] //need i1 - * [_] [`_mm256_mask_compressstoreu_epi64`] //need i1 - * [ ] [`_mm512_mask_compressstoreu_ps`] //need i1 - * [_] [`_mm_mask_compressstoreu_ps`] //need i1 - * [_] [`_mm256_mask_compressstoreu_ps`] //need i1 - * [ ] [`_mm512_mask_compressstoreu_pd`] //need i1 - * [_] [`_mm_mask_compressstoreu_pd`] //need i1 - * [_] [`_mm256_mask_compressstoreu_pd`] //need i1 + * [x] [`_mm512_mask_compressstoreu_epi32`] //need i1 + * [x] [`_mm_mask_compressstoreu_epi32`] //need i1 + * [x] [`_mm256_mask_compressstoreu_epi32`] //need i1 + * [x] [`_mm512_mask_compressstoreu_epi64`] //need i1 + * [x] [`_mm_mask_compressstoreu_epi64`] //need i1 + * [x] [`_mm256_mask_compressstoreu_epi64`] //need i1 + * [x] [`_mm512_mask_compressstoreu_ps`] //need i1 + * [x] [`_mm_mask_compressstoreu_ps`] //need i1 + * [x] [`_mm256_mask_compressstoreu_ps`] //need i1 + * [x] [`_mm512_mask_compressstoreu_pd`] //need i1 + * [x] [`_mm_mask_compressstoreu_pd`] //need i1 + * [x] [`_mm256_mask_compressstoreu_pd`] //need i1 * [x] [`_mm512_mask_expand_epi32`] * [x] [`_mm512_maskz_expand_epi32`] * [x] [`_mm_mask_expand_epi32`] @@ -1665,30 +1665,30 @@ * [x] [`_mm_maskz_expand_pd`] * [x] [`_mm256_mask_expand_pd`] * [x] [`_mm256_maskz_expand_pd`] - * [ ] [`_mm512_mask_expandloadu_epi32`] //need i1 - * [ ] [`_mm512_maskz_expandloadu_epi32`] //need i1 - * [_] [`_mm_mask_expandloadu_epi32`] //need i1 - * [_] [`_mm_maskz_expandloadu_epi32`] //need i1 - * [_] [`_mm256_mask_expandloadu_epi32`] //need i1 - * [_] [`_mm256_maskz_expandloadu_epi32`] //need i1 - * [ ] [`_mm512_mask_expandloadu_epi64`] //need i1 - * [ ] [`_mm512_maskz_expandloadu_epi64`] //need i1 - * [_] [`_mm_mask_expandloadu_epi64`] //need i1 - * [_] [`_mm_maskz_expandloadu_epi64`] //need i1 - * [_] [`_mm256_mask_expandloadu_epi64`] //need i1 - * [_] [`_mm256_maskz_expandloadu_epi64`] //need i1 - * [ ] [`_mm512_mask_expandloadu_ps`] //need i1 - * [ ] [`_mm512_maskz_expandloadu_ps`] //need i1 - * [_] [`_mm_mask_expandloadu_ps`] //need i1 - * [_] [`_mm_maskz_expandloadu_ps`] //need i1 - * [_] [`_mm256_mask_expandloadu_ps`] //need i1 - * [_] [`_mm256_maskz_expandloadu_ps`] //need i1 - * [ ] [`_mm512_mask_expandloadu_pd`] //need i1 - * [ ] [`_mm512_maskz_expandloadu_pd`] //need i1 - * [_] [`_mm_mask_expandloadu_pd`] //need i1 - * [_] [`_mm_maskz_expandloadu_pd`] //need i1 - * [_] [`_mm256_mask_expandloadu_pd`] //need i1 - * [_] [`_mm256_maskz_expandloadu_pd`] //need i1 + * [x] [`_mm512_mask_expandloadu_epi32`] //need i1 + * [x] [`_mm512_maskz_expandloadu_epi32`] //need i1 + * [x] [`_mm_mask_expandloadu_epi32`] //need i1 + * [x] [`_mm_maskz_expandloadu_epi32`] //need i1 + * [x] [`_mm256_mask_expandloadu_epi32`] //need i1 + * [x] [`_mm256_maskz_expandloadu_epi32`] //need i1 + * [x] [`_mm512_mask_expandloadu_epi64`] //need i1 + * [x] [`_mm512_maskz_expandloadu_epi64`] //need i1 + * [x] [`_mm_mask_expandloadu_epi64`] //need i1 + * [x] [`_mm_maskz_expandloadu_epi64`] //need i1 + * [x] [`_mm256_mask_expandloadu_epi64`] //need i1 + * [x] [`_mm256_maskz_expandloadu_epi64`] //need i1 + * [x] [`_mm512_mask_expandloadu_ps`] //need i1 + * [x] [`_mm512_maskz_expandloadu_ps`] //need i1 + * [x] [`_mm_mask_expandloadu_ps`] //need i1 + * [x] [`_mm_maskz_expandloadu_ps`] //need i1 + * [x] [`_mm256_mask_expandloadu_ps`] //need i1 + * [x] [`_mm256_maskz_expandloadu_ps`] //need i1 + * [x] [`_mm512_mask_expandloadu_pd`] //need i1 + * [x] [`_mm512_maskz_expandloadu_pd`] //need i1 + * [x] [`_mm_mask_expandloadu_pd`] //need i1 + * [x] [`_mm_maskz_expandloadu_pd`] //need i1 + * [x] [`_mm256_mask_expandloadu_pd`] //need i1 + * [x] [`_mm256_maskz_expandloadu_pd`] //need i1 * [x] [`_mm512_zextpd128_pd512`] * [x] [`_mm512_zextpd256_pd512`] * [x] [`_mm512_zextps128_ps512`] diff --git a/library/stdarch/crates/core_arch/avx512vbmi2.md b/library/stdarch/crates/core_arch/avx512vbmi2.md deleted file mode 100644 index 693af9d930..0000000000 --- a/library/stdarch/crates/core_arch/avx512vbmi2.md +++ /dev/null @@ -1,153 +0,0 @@ -

["AVX512_VBMI2"]

- - * [x] [`_mm_mask_compress_epi16`] - * [x] [`_mm_maskz_compress_epi16`] - * [x] [`_mm256_mask_compress_epi16`] - * [x] [`_mm256_maskz_compress_epi16`] - * [x] [`_mm512_mask_compress_epi16`] - * [x] [`_mm512_maskz_compress_epi16`] - * [x] [`_mm_mask_compress_epi8`] - * [x] [`_mm_maskz_compress_epi8`] - * [x] [`_mm256_mask_compress_epi8`] - * [x] [`_mm256_maskz_compress_epi8`] - * [x] [`_mm512_mask_compress_epi8`] - * [x] [`_mm512_maskz_compress_epi8`] - * [_] [`_mm_mask_compressstoreu_epi16`] //need i1 - * [_] [`_mm256_mask_compressstoreu_epi16`] //need i1 - * [_] [`_mm512_mask_compressstoreu_epi16`] //need i1 - * [_] [`_mm_mask_compressstoreu_epi8`] //need i1 - * [_] [`_mm256_mask_compressstoreu_epi8`] //need i1 - * [_] [`_mm512_mask_compressstoreu_epi8`] //need i1 - * [x] [`_mm_mask_expand_epi16`] - * [x] [`_mm_maskz_expand_epi16`] - * [x] [`_mm256_mask_expand_epi16`] - * [x] [`_mm256_maskz_expand_epi16`] - * [x] [`_mm512_mask_expand_epi16`] - * [x] [`_mm512_maskz_expand_epi16`] - * [x] [`_mm_mask_expand_epi8`] - * [x] [`_mm_maskz_expand_epi8`] - * [x] [`_mm256_mask_expand_epi8`] - * [x] [`_mm256_maskz_expand_epi8`] - * [x] [`_mm512_mask_expand_epi8`] - * [x] [`_mm512_maskz_expand_epi8`] - * [_] [`_mm_mask_expandloadu_epi16`] //need i1 - * [_] [`_mm_maskz_expandloadu_epi16`] //need i1 - * [_] [`_mm256_mask_expandloadu_epi16`] //need i1 - * [_] [`_mm256_maskz_expandloadu_epi16`] //need i1 - * [_] [`_mm512_mask_expandloadu_epi16`] //need i1 - * [_] [`_mm512_maskz_expandloadu_epi16`] //need i1 - * [_] [`_mm_mask_expandloadu_epi8`] //need i1 - * [_] [`_mm_maskz_expandloadu_epi8`] //need i1 - * [_] [`_mm256_mask_expandloadu_epi8`] //need i1 - * [_] [`_mm256_maskz_expandloadu_epi8`] //need i1 - * [_] [`_mm512_mask_expandloadu_epi8`] //need i1 - * [_] [`_mm512_maskz_expandloadu_epi8`] //need i1 - * [x] [`_mm_mask_shldi_epi16`] - * [x] [`_mm_maskz_shldi_epi16`] - * [x] [`_mm_shldi_epi16`] - * [x] [`_mm256_mask_shldi_epi16`] - * [x] [`_mm256_maskz_shldi_epi16`] - * [x] [`_mm256_shldi_epi16`] - * [x] [`_mm512_mask_shldi_epi16`] - * [x] [`_mm512_maskz_shldi_epi16`] - * [x] [`_mm512_shldi_epi16`] - * [x] [`_mm_mask_shldi_epi32`] - * [x] [`_mm_maskz_shldi_epi32`] - * [x] [`_mm_shldi_epi32`] - * [x] [`_mm256_mask_shldi_epi32`] - * [x] [`_mm256_maskz_shldi_epi32`] - * [x] [`_mm256_shldi_epi32`] - * [x] [`_mm512_mask_shldi_epi32`] - * [x] [`_mm512_maskz_shldi_epi32`] - * [x] [`_mm512_shldi_epi32`] - * [x] [`_mm_mask_shldi_epi64`] - * [x] [`_mm_maskz_shldi_epi64`] - * [x] [`_mm_shldi_epi64`] - * [x] [`_mm256_mask_shldi_epi64`] - * [x] [`_mm256_maskz_shldi_epi64`] - * [x] [`_mm256_shldi_epi64`] - * [x] [`_mm512_mask_shldi_epi64`] - * [x] [`_mm512_maskz_shldi_epi64`] - * [x] [`_mm512_shldi_epi64`] - * [x] [`_mm_mask_shldv_epi16`] - * [x] [`_mm_maskz_shldv_epi16`] - * [x] [`_mm_shldv_epi16`] - * [x] [`_mm256_mask_shldv_epi16`] - * [x] [`_mm256_maskz_shldv_epi16`] - * [x] [`_mm256_shldv_epi16`] - * [x] [`_mm512_mask_shldv_epi16`] - * [x] [`_mm512_maskz_shldv_epi16`] - * [x] [`_mm512_shldv_epi16`] - * [x] [`_mm_mask_shldv_epi32`] - * [x] [`_mm_maskz_shldv_epi32`] - * [x] [`_mm_shldv_epi32`] - * [x] [`_mm256_mask_shldv_epi32`] - * [x] [`_mm256_maskz_shldv_epi32`] - * [x] [`_mm256_shldv_epi32`] - * [x] [`_mm512_mask_shldv_epi32`] - * [x] [`_mm512_maskz_shldv_epi32`] - * [x] [`_mm512_shldv_epi32`] - * [x] [`_mm_mask_shldv_epi64`] - * [x] [`_mm_maskz_shldv_epi64`] - * [x] [`_mm_shldv_epi64`] - * [x] [`_mm256_mask_shldv_epi64`] - * [x] [`_mm256_maskz_shldv_epi64`] - * [x] [`_mm256_shldv_epi64`] - * [x] [`_mm512_mask_shldv_epi64`] - * [x] [`_mm512_maskz_shldv_epi64`] - * [x] [`_mm512_shldv_epi64`] - * [x] [`_mm_mask_shrdi_epi16`] - * [x] [`_mm_maskz_shrdi_epi16`] - * [x] [`_mm_shrdi_epi16`] - * [x] [`_mm256_mask_shrdi_epi16`] - * [x] [`_mm256_maskz_shrdi_epi16`] - * [x] [`_mm256_shrdi_epi16`] - * [x] [`_mm512_mask_shrdi_epi16`] - * [x] [`_mm512_maskz_shrdi_epi16`] - * [x] [`_mm512_shrdi_epi16`] - * [x] [`_mm_mask_shrdi_epi32`] - * [x] [`_mm_maskz_shrdi_epi32`] - * [x] [`_mm_shrdi_epi32`] - * [x] [`_mm256_mask_shrdi_epi32`] - * [x] [`_mm256_maskz_shrdi_epi32`] - * [x] [`_mm256_shrdi_epi32`] - * [x] [`_mm512_mask_shrdi_epi32`] - * [x] [`_mm512_maskz_shrdi_epi32`] - * [x] [`_mm512_shrdi_epi32`] - * [x] [`_mm_mask_shrdi_epi64`] - * [x] [`_mm_maskz_shrdi_epi64`] - * [x] [`_mm_shrdi_epi64`] - * [x] [`_mm256_mask_shrdi_epi64`] - * [x] [`_mm256_maskz_shrdi_epi64`] - * [x] [`_mm256_shrdi_epi64`] - * [x] [`_mm512_mask_shrdi_epi64`] - * [x] [`_mm512_maskz_shrdi_epi64`] - * [x] [`_mm512_shrdi_epi64`] - * [x] [`_mm_mask_shrdv_epi16`] - * [x] [`_mm_maskz_shrdv_epi16`] - * [x] [`_mm_shrdv_epi16`] - * [x] [`_mm256_mask_shrdv_epi16`] - * [x] [`_mm256_maskz_shrdv_epi16`] - * [x] [`_mm256_shrdv_epi16`] - * [x] [`_mm512_mask_shrdv_epi16`] - * [x] [`_mm512_maskz_shrdv_epi16`] - * [x] [`_mm512_shrdv_epi16`] - * [x] [`_mm_mask_shrdv_epi32`] - * [x] [`_mm_maskz_shrdv_epi32`] - * [x] [`_mm_shrdv_epi32`] - * [x] [`_mm256_mask_shrdv_epi32`] - * [x] [`_mm256_maskz_shrdv_epi32`] - * [x] [`_mm256_shrdv_epi32`] - * [x] [`_mm512_mask_shrdv_epi32`] - * [x] [`_mm512_maskz_shrdv_epi32`] - * [x] [`_mm512_shrdv_epi32`] - * [x] [`_mm_mask_shrdv_epi64`] - * [x] [`_mm_maskz_shrdv_epi64`] - * [x] [`_mm_shrdv_epi64`] - * [x] [`_mm256_mask_shrdv_epi64`] - * [x] [`_mm256_maskz_shrdv_epi64`] - * [x] [`_mm256_shrdv_epi64`] - * [x] [`_mm512_mask_shrdv_epi64`] - * [x] [`_mm512_maskz_shrdv_epi64`] - * [x] [`_mm512_shrdv_epi64`] -

diff --git a/library/stdarch/crates/core_arch/src/core_arch_docs.md b/library/stdarch/crates/core_arch/src/core_arch_docs.md index 58b7eda9a8..eddd1fc0c7 100644 --- a/library/stdarch/crates/core_arch/src/core_arch_docs.md +++ b/library/stdarch/crates/core_arch/src/core_arch_docs.md @@ -194,18 +194,18 @@ others at: * [`nvptx`] * [`wasm32`] -[`x86`]: x86/index.html -[`x86_64`]: x86_64/index.html -[`arm`]: arm/index.html -[`aarch64`]: aarch64/index.html -[`riscv32`]: riscv32/index.html -[`riscv64`]: riscv64/index.html -[`mips`]: mips/index.html -[`mips64`]: mips64/index.html -[`powerpc`]: powerpc/index.html -[`powerpc64`]: powerpc64/index.html -[`nvptx`]: nvptx/index.html -[`wasm32`]: wasm32/index.html +[`x86`]: ../../core/arch/x86/index.html +[`x86_64`]: ../../core/arch/x86_64/index.html +[`arm`]: ../../core/arch/arm/index.html +[`aarch64`]: ../../core/arch/aarch64/index.html +[`riscv32`]: ../../core/arch/riscv32/index.html +[`riscv64`]: ../../core/arch/riscv64/index.html +[`mips`]: ../../core/arch/mips/index.html +[`mips64`]: ../../core/arch/mips64/index.html +[`powerpc`]: ../../core/arch/powerpc/index.html +[`powerpc64`]: ../../core/arch/powerpc64/index.html +[`nvptx`]: ../../core/arch/nvptx/index.html +[`wasm32`]: ../../core/arch/wasm32/index.html # Examples diff --git a/library/stdarch/crates/core_arch/src/riscv64/mod.rs b/library/stdarch/crates/core_arch/src/riscv64/mod.rs index 24aae78325..751b9a860f 100644 --- a/library/stdarch/crates/core_arch/src/riscv64/mod.rs +++ b/library/stdarch/crates/core_arch/src/riscv64/mod.rs @@ -10,7 +10,7 @@ use crate::arch::asm; /// This operation is not available under RV32 base instruction set. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLV.WU` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlv_wu(src: *const u32) -> u32 { let value: u32; @@ -18,7 +18,7 @@ pub unsafe fn hlv_wu(src: *const u32) -> u32 { value } -/// Loads virtual machine memory by unsigned double integer +/// Loads virtual machine memory by double integer /// /// This instruction performs an explicit memory access as though `V=1`; /// i.e., with the address translation and protection, and the endianness, that apply to memory @@ -27,7 +27,7 @@ pub unsafe fn hlv_wu(src: *const u32) -> u32 { /// This operation is not available under RV32 base instruction set. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLV.D` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlv_d(src: *const i64) -> i64 { let value: i64; @@ -42,7 +42,7 @@ pub unsafe fn hlv_d(src: *const i64) -> i64 { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HSV.D` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hsv_d(dst: *mut i64, src: i64) { asm!(".insn r 0x73, 0x4, 0x37, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); diff --git a/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs b/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs index a2c9cb2436..347735df1d 100644 --- a/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs +++ b/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs @@ -153,7 +153,7 @@ pub unsafe fn sfence_inval_ir() { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLV.B` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlv_b(src: *const i8) -> i8 { let value: i8; @@ -168,7 +168,7 @@ pub unsafe fn hlv_b(src: *const i8) -> i8 { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLV.BU` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlv_bu(src: *const u8) -> u8 { let value: u8; @@ -183,7 +183,7 @@ pub unsafe fn hlv_bu(src: *const u8) -> u8 { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLV.H` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlv_h(src: *const i16) -> i16 { let value: i16; @@ -198,7 +198,7 @@ pub unsafe fn hlv_h(src: *const i16) -> i16 { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLV.HU` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlv_hu(src: *const u16) -> u16 { let value: u16; @@ -213,7 +213,7 @@ pub unsafe fn hlv_hu(src: *const u16) -> u16 { /// but read permission is not required. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLVX.HU` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlvx_hu(src: *const u16) -> u16 { let insn: u16; @@ -228,7 +228,7 @@ pub unsafe fn hlvx_hu(src: *const u16) -> u16 { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLV.W` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlv_w(src: *const i32) -> i32 { let value: i32; @@ -243,7 +243,7 @@ pub unsafe fn hlv_w(src: *const i32) -> i32 { /// but read permission is not required. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HLVX.WU` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hlvx_wu(src: *const u32) -> u32 { let insn: u32; @@ -258,7 +258,7 @@ pub unsafe fn hlvx_wu(src: *const u32) -> u32 { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HSV.B` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hsv_b(dst: *mut i8, src: i8) { asm!(".insn r 0x73, 0x4, 0x31, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); @@ -271,7 +271,7 @@ pub unsafe fn hsv_b(dst: *mut i8, src: i8) { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HSV.H` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hsv_h(dst: *mut i16, src: i16) { asm!(".insn r 0x73, 0x4, 0x33, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); @@ -284,7 +284,7 @@ pub unsafe fn hsv_h(dst: *mut i16, src: i16) { /// accesses in either VS-mode or VU-mode. /// /// This function is unsafe for it accesses the virtual supervisor or user via a `HSV.W` -/// instruction which is effectively an unreference to any memory address. +/// instruction which is effectively a dereference to any memory address. #[inline] pub unsafe fn hsv_w(dst: *mut i32, src: i32) { asm!(".insn r 0x73, 0x4, 0x35, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); @@ -469,6 +469,111 @@ pub unsafe fn hinval_gvma_vmid(vmid: usize) { asm!(".insn r 0x73, 0, 0x33, x0, x0, {}", in(reg) vmid, options(nostack)) } +/// Reads the floating-point control and status register `fcsr` +/// +/// Register `fcsr` is a 32-bit read/write register that selects the dynamic rounding mode +/// for floating-point arithmetic operations and holds the accrued exception flag. +/// +/// Accoding to "F" Standard Extension for Single-Precision Floating-Point, Version 2.2, +/// register `fcsr` is defined as: +/// +/// | Bit index | Meaning | +/// |:----------|:--------| +/// | 0..=4 | Accrued Exceptions (`fflags`) | +/// | 5..=7 | Rounding Mode (`frm`) | +/// | 8..=31 | _Reserved_ | +/// +/// For definition of each field, visit [`frrm`] and [`frflags`]. +/// +/// [`frrm`]: fn.frrm.html +/// [`frflags`]: fn.frflags.html +#[inline] +pub fn frcsr() -> u32 { + let value: u32; + unsafe { asm!("frcsr {}", out(reg) value, options(nomem, nostack)) }; + value +} + +/// Swaps the floating-point control and status register `fcsr` +/// +/// This function swaps the value in `fcsr` by copying the original value to be returned, +/// and then writing a new value obtained from input variable `value` into `fcsr`. +#[inline] +pub fn fscsr(value: u32) -> u32 { + let original: u32; + unsafe { asm!("fscsr {}, {}", out(reg) original, in(reg) value, options(nomem, nostack)) } + original +} + +/// Reads the floating-point rounding mode register `frm` +/// +/// Accoding to "F" Standard Extension for Single-Precision Floating-Point, Version 2.2, +/// the rounding mode field is defined as listed in the table below: +/// +/// | Rounding Mode | Mnemonic | Meaning | +/// |:-------------|:----------|:---------| +/// | 000 | RNE | Round to Nearest, ties to Even | +/// | 001 | RTZ | Round towards Zero | +/// | 010 | RDN | Round Down (towards −∞) | +/// | 011 | RUP | Round Up (towards +∞) | +/// | 100 | RMM | Round to Nearest, ties to Max Magnitude | +/// | 101 | | _Reserved for future use._ | +/// | 110 | | _Reserved for future use._ | +/// | 111 | DYN | In Rounding Mode register, _reserved_. | +#[inline] +pub fn frrm() -> u32 { + let value: u32; + unsafe { asm!("frrm {}", out(reg) value, options(nomem, nostack)) }; + value +} + +/// Swaps the floating-point rounding mode register `frm` +/// +/// This function swaps the value in `frm` by copying the original value to be returned, +/// and then writing a new value obtained from the three least-significant bits of +/// input variable `value` into `frm`. +#[inline] +pub fn fsrm(value: u32) -> u32 { + let original: u32; + unsafe { asm!("fsrm {}, {}", out(reg) original, in(reg) value, options(nomem, nostack)) } + original +} + +/// Reads the floating-point accrued exception flags register `fflags` +/// +/// The accrued exception flags indicate the exception conditions that have arisen +/// on any floating-point arithmetic instruction since the field was last reset by software. +/// +/// Accoding to "F" Standard Extension for Single-Precision Floating-Point, Version 2.2, +/// the accured exception flags is defined as a bit vector of 5 bits. +/// The meaning of each binary bit is listed in the table below. +/// +/// | Bit index | Mnemonic | Meaning | +/// |:--|:---|:-----------------| +/// | 4 | NV | Invalid Operation | +/// | 3 | DZ | Divide by Zero | +/// | 2 | OF | Overflow | +/// | 1 | UF | Underflow | +/// | 0 | NX | Inexact | +#[inline] +pub fn frflags() -> u32 { + let value: u32; + unsafe { asm!("frflags {}", out(reg) value, options(nomem, nostack)) }; + value +} + +/// Swaps the floating-point accrued exception flags register `fflags` +/// +/// This function swaps the value in `fflags` by copying the original value to be returned, +/// and then writing a new value obtained from the five least-significant bits of +/// input variable `value` into `fflags`. +#[inline] +pub fn fsflags(value: u32) -> u32 { + let original: u32; + unsafe { asm!("fsflags {}, {}", out(reg) original, in(reg) value, options(nomem, nostack)) } + original +} + /// Invalidate hypervisor translation cache for all virtual machines and guest physical addresses /// /// This instruction invalidates any address-translation cache entries that an @@ -479,3 +584,188 @@ pub unsafe fn hinval_gvma_vmid(vmid: usize) { pub unsafe fn hinval_gvma_all() { asm!(".insn r 0x73, 0, 0x33, x0, x0, x0", options(nostack)) } + +/// `P0` transformation function as is used in the SM3 hash algorithm +/// +/// This function is included in `Zksh` extension. It's defined as: +/// +/// ```text +/// P0(X) = X ⊕ (X ≪ 9) ⊕ (X ≪ 17) +/// ``` +/// +/// where `⊕` represents 32-bit xor, and `≪ k` represents rotate left by `k` bits. +/// +/// In the SM3 algorithm, the `P0` transformation is used as `E ← P0(TT2)` when the +/// compression function `CF` uses the intermediate value `TT2` to calculate +/// the variable `E` in one iteration for subsequent processes. +/// +/// According to RISC-V Cryptography Extensions, Volume I, the execution latency of +/// this instruction must always be independent from the data it operates on. +#[inline] +pub fn sm3p0(x: u32) -> u32 { + let ans: u32; + unsafe { + // asm!("sm3p0 {}, {}", out(reg) ans, in(reg) x, options(nomem, nostack)) + asm!(".insn i 0x13, 0x1, {}, {}, 0x108", out(reg) ans, in(reg) x, options(nomem, nostack)) + }; + ans +} + +/// `P1` transformation function as is used in the SM3 hash algorithm +/// +/// This function is included in `Zksh` extension. It's defined as: +/// +/// ```text +/// P1(X) = X ⊕ (X ≪ 15) ⊕ (X ≪ 23) +/// ``` +/// +/// where `⊕` represents 32-bit xor, and `≪ k` represents rotate left by `k` bits. +/// +/// In the SM3 algorithm, the `P1` transformation is used to expand message, +/// where expanded word `Wj` can be generated from the previous words. +/// The whole process can be described as the following pseudocode: +/// +/// ```text +/// FOR j=16 TO 67 +/// Wj ← P1(Wj−16 ⊕ Wj−9 ⊕ (Wj−3 ≪ 15)) ⊕ (Wj−13 ≪ 7) ⊕ Wj−6 +/// ENDFOR +/// ``` +/// +/// According to RISC-V Cryptography Extensions, Volume I, the execution latency of +/// this instruction must always be independent from the data it operates on. +#[inline] +pub fn sm3p1(x: u32) -> u32 { + let ans: u32; + unsafe { + // asm!("sm3p1 {}, {}", out(reg) ans, in(reg) x, options(nomem, nostack)) + asm!(".insn i 0x13, 0x1, {}, {}, 0x109", out(reg) ans, in(reg) x, options(nomem, nostack)) + }; + ans +} + +/// Accelerates the round function `F` in the SM4 block cipher algorithm +/// +/// This instruction is included in extension `Zksed`. It's defined as: +/// +/// ```text +/// SM4ED(x, a, BS) = x ⊕ T(ai) +/// ... where +/// ai = a.bytes[BS] +/// T(ai) = L(τ(ai)) +/// bi = τ(ai) = SM4-S-Box(ai) +/// ci = L(bi) = bi ⊕ (bi ≪ 2) ⊕ (bi ≪ 10) ⊕ (bi ≪ 18) ⊕ (bi ≪ 24) +/// SM4ED = (ci ≪ (BS * 8)) ⊕ x +/// ``` +/// +/// where `⊕` represents 32-bit xor, and `≪ k` represents rotate left by `k` bits. +/// As is defined above, `T` is a combined transformation of non linear S-Box transform `τ` +/// and linear layer transform `L`. +/// +/// In the SM4 algorithm, the round function `F` is defined as: +/// +/// ```text +/// F(x0, x1, x2, x3, rk) = x0 ⊕ T(x1 ⊕ x2 ⊕ x3 ⊕ rk) +/// ... where +/// T(A) = L(τ(A)) +/// B = τ(A) = (SM4-S-Box(a0), SM4-S-Box(a1), SM4-S-Box(a2), SM4-S-Box(a3)) +/// C = L(B) = B ⊕ (B ≪ 2) ⊕ (B ≪ 10) ⊕ (B ≪ 18) ⊕ (B ≪ 24) +/// ``` +/// +/// It can be implemented by `sm4ed` instruction like: +/// +/// ```no_run +/// let a = x1 ^ x2 ^ x3 ^ rk; +/// let c0 = sm4ed::<0>(x0, a); +/// let c1 = sm4ed::<1>(c0, a); // c1 represents c[0..=1], etc. +/// let c2 = sm4ed::<2>(c1, a); +/// let c3 = sm4ed::<3>(c2, a); +/// return c3; // c3 represents c[0..=3] +/// ``` +/// +/// According to RISC-V Cryptography Extensions, Volume I, the execution latency of +/// this instruction must always be independent from the data it operates on. +pub fn sm4ed(x: u32, a: u32) -> u32 { + static_assert!(BS: u8 where BS <= 3); + let ans: u32; + match BS { + 0 => unsafe { + asm!(".insn r 0x33, 0, 0x18, {}, {}, {}", out(reg) ans, in(reg) x, in(reg) a, options(nomem, nostack)) + }, + 1 => unsafe { + asm!(".insn r 0x33, 0, 0x38, {}, {}, {}", out(reg) ans, in(reg) x, in(reg) a, options(nomem, nostack)) + }, + 2 => unsafe { + asm!(".insn r 0x33, 0, 0x58, {}, {}, {}", out(reg) ans, in(reg) x, in(reg) a, options(nomem, nostack)) + }, + 3 => unsafe { + asm!(".insn r 0x33, 0, 0x78, {}, {}, {}", out(reg) ans, in(reg) x, in(reg) a, options(nomem, nostack)) + }, + _ => unreachable!(), + }; + ans +} + +/// Accelerates the key schedule operation in the SM4 block cipher algorithm +/// +/// This instruction is included in extension `Zksed`. It's defined as: +/// +/// ```text +/// SM4KS(x, k, BS) = x ⊕ T'(ki) +/// ... where +/// ki = k.bytes[BS] +/// T'(ki) = L'(τ(ki)) +/// bi = τ(ki) = SM4-S-Box(ki) +/// ci = L'(bi) = bi ⊕ (bi ≪ 13) ⊕ (bi ≪ 23) +/// SM4KS = (ci ≪ (BS * 8)) ⊕ x +/// ``` +/// +/// where `⊕` represents 32-bit xor, and `≪ k` represents rotate left by `k` bits. +/// As is defined above, `T'` is a combined transformation of non linear S-Box transform `τ` +/// and the replaced linear layer transform `L'`. +/// +/// In the SM4 algorithm, the key schedule is defined as: +/// +/// ```text +/// rk[i] = K[i+4] = K[i] ⊕ T'(K[i+1] ⊕ K[i+2] ⊕ K[i+3] ⊕ CK[i]) +/// ... where +/// K[0..=3] = MK[0..=3] ⊕ FK[0..=3] +/// T'(K) = L'(τ(K)) +/// B = τ(K) = (SM4-S-Box(k0), SM4-S-Box(k1), SM4-S-Box(k2), SM4-S-Box(k3)) +/// C = L'(B) = B ⊕ (B ≪ 13) ⊕ (B ≪ 23) +/// ``` +/// +/// where `MK` represents the input 128-bit encryption key, +/// constants `FK` and `CK` are fixed system configuration constant values defined by the SM4 algorithm. +/// Hence, the key schedule operation can be implemented by `sm4ks` instruction like: +/// +/// ```no_run +/// let k = k1 ^ k2 ^ k3 ^ ck_i; +/// let c0 = sm4ks::<0>(k0, k); +/// let c1 = sm4ks::<1>(c0, k); // c1 represents c[0..=1], etc. +/// let c2 = sm4ks::<2>(c1, k); +/// let c3 = sm4ks::<3>(c2, k); +/// return c3; // c3 represents c[0..=3] +/// ``` +/// +/// According to RISC-V Cryptography Extensions, Volume I, the execution latency of +/// this instruction must always be independent from the data it operates on. +pub fn sm4ks(x: u32, k: u32) -> u32 { + static_assert!(BS: u8 where BS <= 3); + let ans: u32; + match BS { + 0 => unsafe { + asm!(".insn r 0x33, 0, 0x1A, {}, {}, {}", out(reg) ans, in(reg) x, in(reg) k, options(nomem, nostack)) + }, + 1 => unsafe { + asm!(".insn r 0x33, 0, 0x3A, {}, {}, {}", out(reg) ans, in(reg) x, in(reg) k, options(nomem, nostack)) + }, + 2 => unsafe { + asm!(".insn r 0x33, 0, 0x5A, {}, {}, {}", out(reg) ans, in(reg) x, in(reg) k, options(nomem, nostack)) + }, + 3 => unsafe { + asm!(".insn r 0x33, 0, 0x7A, {}, {}, {}", out(reg) ans, in(reg) x, in(reg) k, options(nomem, nostack)) + }, + _ => unreachable!(), + }; + ans +} diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index 97c6f6c4d6..f70a284667 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -16156,6 +16156,126 @@ pub unsafe fn _mm_maskz_compress_pd(k: __mmask8, a: __m128d) -> __m128d { transmute(vcompresspd128(a.as_f64x2(), _mm_setzero_pd().as_f64x2(), k)) } +/// Contiguously store the active 32-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_compressstoreu_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcompressd))] +pub unsafe fn _mm512_mask_compressstoreu_epi32(base_addr: *mut u8, k: __mmask16, a: __m512i) { + vcompressstored(base_addr as *mut _, a.as_i32x16(), k) +} + +/// Contiguously store the active 32-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_compressstoreu_epi32) +#[inline] +#[target_feature(enable = "avx512f,avx512vl")] +#[cfg_attr(test, assert_instr(vpcompressd))] +pub unsafe fn _mm256_mask_compressstoreu_epi32(base_addr: *mut u8, k: __mmask8, a: __m256i) { + vcompressstored256(base_addr as *mut _, a.as_i32x8(), k) +} + +/// Contiguously store the active 32-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_compressstoreu_epi32) +#[inline] +#[target_feature(enable = "avx512f,avx512vl")] +#[cfg_attr(test, assert_instr(vpcompressd))] +pub unsafe fn _mm_mask_compressstoreu_epi32(base_addr: *mut u8, k: __mmask8, a: __m128i) { + vcompressstored128(base_addr as *mut _, a.as_i32x4(), k) +} + +/// Contiguously store the active 64-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_compressstoreu_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcompressq))] +pub unsafe fn _mm512_mask_compressstoreu_epi64(base_addr: *mut u8, k: __mmask8, a: __m512i) { + vcompressstoreq(base_addr as *mut _, a.as_i64x8(), k) +} + +/// Contiguously store the active 64-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_compressstoreu_epi64) +#[inline] +#[target_feature(enable = "avx512f,avx512vl")] +#[cfg_attr(test, assert_instr(vpcompressq))] +pub unsafe fn _mm256_mask_compressstoreu_epi64(base_addr: *mut u8, k: __mmask8, a: __m256i) { + vcompressstoreq256(base_addr as *mut _, a.as_i64x4(), k) +} + +/// Contiguously store the active 64-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_compressstoreu_epi64) +#[inline] +#[target_feature(enable = "avx512f,avx512vl")] +#[cfg_attr(test, assert_instr(vpcompressq))] +pub unsafe fn _mm_mask_compressstoreu_epi64(base_addr: *mut u8, k: __mmask8, a: __m128i) { + vcompressstoreq128(base_addr as *mut _, a.as_i64x2(), k) +} + +/// Contiguously store the active single-precision (32-bit) floating-point elements in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_compressstoreu_ps) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcompressps))] +pub unsafe fn _mm512_mask_compressstoreu_ps(base_addr: *mut u8, k: __mmask16, a: __m512) { + vcompressstoreps(base_addr as *mut _, a.as_f32x16(), k) +} + +/// Contiguously store the active single-precision (32-bit) floating-point elements in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_compressstoreu_ps) +#[inline] +#[target_feature(enable = "avx512f,avx512vl")] +#[cfg_attr(test, assert_instr(vcompressps))] +pub unsafe fn _mm256_mask_compressstoreu_ps(base_addr: *mut u8, k: __mmask8, a: __m256) { + vcompressstoreps256(base_addr as *mut _, a.as_f32x8(), k) +} + +/// Contiguously store the active single-precision (32-bit) floating-point elements in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_compressstoreu_ps) +#[inline] +#[target_feature(enable = "avx512f,avx512vl")] +#[cfg_attr(test, assert_instr(vcompressps))] +pub unsafe fn _mm_mask_compressstoreu_ps(base_addr: *mut u8, k: __mmask8, a: __m128) { + vcompressstoreps128(base_addr as *mut _, a.as_f32x4(), k) +} + +/// Contiguously store the active double-precision (64-bit) floating-point elements in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_compressstoreu_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcompresspd))] +pub unsafe fn _mm512_mask_compressstoreu_pd(base_addr: *mut u8, k: __mmask8, a: __m512d) { + vcompressstorepd(base_addr as *mut _, a.as_f64x8(), k) +} + +/// Contiguously store the active double-precision (64-bit) floating-point elements in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_compressstoreu_pd) +#[inline] +#[target_feature(enable = "avx512f,avx512vl")] +#[cfg_attr(test, assert_instr(vcompresspd))] +pub unsafe fn _mm256_mask_compressstoreu_pd(base_addr: *mut u8, k: __mmask8, a: __m256d) { + vcompressstorepd256(base_addr as *mut _, a.as_f64x4(), k) +} + +/// Contiguously store the active double-precision (64-bit) floating-point elements in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_compressstoreu_pd) +#[inline] +#[target_feature(enable = "avx512f,avx512vl")] +#[cfg_attr(test, assert_instr(vcompresspd))] +pub unsafe fn _mm_mask_compressstoreu_pd(base_addr: *mut u8, k: __mmask8, a: __m128d) { + vcompressstorepd128(base_addr as *mut _, a.as_f64x2(), k) +} + /// Load contiguous active 32-bit integers from a (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). /// /// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_expand_epi32&expand=2316) @@ -31651,6 +31771,450 @@ pub unsafe fn _mm_mask_store_pd(mem_addr: *mut f64, mask: __mmask8, a: __m128d) ); } +/// Load contiguous active 32-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_expandloadu_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_mask_expandloadu_epi32( + src: __m512i, + k: __mmask16, + mem_addr: *const i32, +) -> __m512i { + let mut dst: __m512i = src; + asm!( + vpl!("vpexpandd {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 32-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_expandloadu_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_maskz_expandloadu_epi32(k: __mmask16, mem_addr: *const i32) -> __m512i { + let mut dst: __m512i; + asm!( + vpl!("vpexpandd {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 32-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_expandloadu_epi32) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx")] +pub unsafe fn _mm256_mask_expandloadu_epi32( + src: __m256i, + k: __mmask8, + mem_addr: *const i32, +) -> __m256i { + let mut dst: __m256i = src; + asm!( + vpl!("vpexpandd {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 32-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_maskz_expandloadu_epi32) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx")] +pub unsafe fn _mm256_maskz_expandloadu_epi32(k: __mmask8, mem_addr: *const i32) -> __m256i { + let mut dst: __m256i; + asm!( + vpl!("vpexpandd {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 32-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_expandloadu_epi32) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx,sse")] +pub unsafe fn _mm_mask_expandloadu_epi32( + src: __m128i, + k: __mmask8, + mem_addr: *const i32, +) -> __m128i { + let mut dst: __m128i = src; + asm!( + vpl!("vpexpandd {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 32-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maskz_expandloadu_epi32) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx,sse")] +pub unsafe fn _mm_maskz_expandloadu_epi32(k: __mmask8, mem_addr: *const i32) -> __m128i { + let mut dst: __m128i; + asm!( + vpl!("vpexpandd {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 64-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_expandloadu_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_mask_expandloadu_epi64( + src: __m512i, + k: __mmask8, + mem_addr: *const i64, +) -> __m512i { + let mut dst: __m512i = src; + asm!( + vpl!("vpexpandq {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 64-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_expandloadu_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_maskz_expandloadu_epi64(k: __mmask8, mem_addr: *const i64) -> __m512i { + let mut dst: __m512i; + asm!( + vpl!("vpexpandq {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 64-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_expandloadu_epi64) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx")] +pub unsafe fn _mm256_mask_expandloadu_epi64( + src: __m256i, + k: __mmask8, + mem_addr: *const i64, +) -> __m256i { + let mut dst: __m256i = src; + asm!( + vpl!("vpexpandq {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 64-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_maskz_expandloadu_epi64) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx")] +pub unsafe fn _mm256_maskz_expandloadu_epi64(k: __mmask8, mem_addr: *const i64) -> __m256i { + let mut dst: __m256i; + asm!( + vpl!("vpexpandq {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 64-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_expandloadu_epi64) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx,sse")] +pub unsafe fn _mm_mask_expandloadu_epi64( + src: __m128i, + k: __mmask8, + mem_addr: *const i64, +) -> __m128i { + let mut dst: __m128i = src; + asm!( + vpl!("vpexpandq {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 64-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maskz_expandloadu_epi64) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx,sse")] +pub unsafe fn _mm_maskz_expandloadu_epi64(k: __mmask8, mem_addr: *const i64) -> __m128i { + let mut dst: __m128i; + asm!( + vpl!("vpexpandq {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (32-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_expandloadu_ps) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_mask_expandloadu_ps( + src: __m512, + k: __mmask16, + mem_addr: *const f32, +) -> __m512 { + let mut dst: __m512 = src; + asm!( + vpl!("vexpandps {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (32-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_expandloadu_ps) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_maskz_expandloadu_ps(k: __mmask16, mem_addr: *const f32) -> __m512 { + let mut dst: __m512; + asm!( + vpl!("vexpandps {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (32-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_expandloadu_ps) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx")] +pub unsafe fn _mm256_mask_expandloadu_ps(src: __m256, k: __mmask8, mem_addr: *const f32) -> __m256 { + let mut dst: __m256 = src; + asm!( + vpl!("vexpandps {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (32-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_maskz_expandloadu_ps) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx")] +pub unsafe fn _mm256_maskz_expandloadu_ps(k: __mmask8, mem_addr: *const f32) -> __m256 { + let mut dst: __m256; + asm!( + vpl!("vexpandps {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (32-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_expandloadu_ps) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx,sse")] +pub unsafe fn _mm_mask_expandloadu_ps(src: __m128, k: __mmask8, mem_addr: *const f32) -> __m128 { + let mut dst: __m128 = src; + asm!( + vpl!("vexpandps {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (32-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maskz_expandloadu_ps) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx,sse")] +pub unsafe fn _mm_maskz_expandloadu_ps(k: __mmask8, mem_addr: *const f32) -> __m128 { + let mut dst: __m128; + asm!( + vpl!("vexpandps {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (64-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_expandloadu_pd) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_mask_expandloadu_pd( + src: __m512d, + k: __mmask8, + mem_addr: *const f64, +) -> __m512d { + let mut dst: __m512d = src; + asm!( + vpl!("vexpandpd {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (64-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_expandloadu_pd) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_maskz_expandloadu_pd(k: __mmask8, mem_addr: *const f64) -> __m512d { + let mut dst: __m512d; + asm!( + vpl!("vexpandpd {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (64-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_expandloadu_pd) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx")] +pub unsafe fn _mm256_mask_expandloadu_pd( + src: __m256d, + k: __mmask8, + mem_addr: *const f64, +) -> __m256d { + let mut dst: __m256d = src; + asm!( + vpl!("vexpandpd {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (64-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_maskz_expandloadu_pd) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx")] +pub unsafe fn _mm256_maskz_expandloadu_pd(k: __mmask8, mem_addr: *const f64) -> __m256d { + let mut dst: __m256d; + asm!( + vpl!("vexpandpd {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (64-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_expandloadu_pd) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx,sse")] +pub unsafe fn _mm_mask_expandloadu_pd(src: __m128d, k: __mmask8, mem_addr: *const f64) -> __m128d { + let mut dst: __m128d = src; + asm!( + vpl!("vexpandpd {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active single-precision (64-bit) floating-point elements from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maskz_expandloadu_pd) +#[inline] +#[target_feature(enable = "avx512f,avx512vl,avx,sse")] +pub unsafe fn _mm_maskz_expandloadu_pd(k: __mmask8, mem_addr: *const f64) -> __m128d { + let mut dst: __m128d; + asm!( + vpl!("vexpandpd {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + /// Set packed double-precision (64-bit) floating-point elements in dst with the supplied values in reverse order. /// /// [Intel's documentation]( https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_setr_pd&expand=5002) @@ -38007,6 +38571,34 @@ extern "C" { #[link_name = "llvm.x86.avx512.mask.compress.pd.128"] fn vcompresspd128(a: f64x2, src: f64x2, mask: u8) -> f64x2; + #[link_name = "llvm.x86.avx512.mask.compress.store.d.512"] + fn vcompressstored(mem: *mut i8, data: i32x16, mask: u16); + #[link_name = "llvm.x86.avx512.mask.compress.store.d.256"] + fn vcompressstored256(mem: *mut i8, data: i32x8, mask: u8); + #[link_name = "llvm.x86.avx512.mask.compress.store.d.128"] + fn vcompressstored128(mem: *mut i8, data: i32x4, mask: u8); + + #[link_name = "llvm.x86.avx512.mask.compress.store.q.512"] + fn vcompressstoreq(mem: *mut i8, data: i64x8, mask: u8); + #[link_name = "llvm.x86.avx512.mask.compress.store.q.256"] + fn vcompressstoreq256(mem: *mut i8, data: i64x4, mask: u8); + #[link_name = "llvm.x86.avx512.mask.compress.store.q.128"] + fn vcompressstoreq128(mem: *mut i8, data: i64x2, mask: u8); + + #[link_name = "llvm.x86.avx512.mask.compress.store.ps.512"] + fn vcompressstoreps(mem: *mut i8, data: f32x16, mask: u16); + #[link_name = "llvm.x86.avx512.mask.compress.store.ps.256"] + fn vcompressstoreps256(mem: *mut i8, data: f32x8, mask: u8); + #[link_name = "llvm.x86.avx512.mask.compress.store.ps.128"] + fn vcompressstoreps128(mem: *mut i8, data: f32x4, mask: u8); + + #[link_name = "llvm.x86.avx512.mask.compress.store.pd.512"] + fn vcompressstorepd(mem: *mut i8, data: f64x8, mask: u8); + #[link_name = "llvm.x86.avx512.mask.compress.store.pd.256"] + fn vcompressstorepd256(mem: *mut i8, data: f64x4, mask: u8); + #[link_name = "llvm.x86.avx512.mask.compress.store.pd.128"] + fn vcompressstorepd128(mem: *mut i8, data: f64x2, mask: u8); + #[link_name = "llvm.x86.avx512.mask.expand.d.512"] fn vpexpandd(a: i32x16, src: i32x16, mask: u16) -> i32x16; #[link_name = "llvm.x86.avx512.mask.expand.d.256"] @@ -51357,6 +51949,138 @@ mod tests { assert_eq_m128(r, e); } + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_compressstoreu_epi32() { + let a = _mm512_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let mut r = [0_i32; 16]; + _mm512_mask_compressstoreu_epi32(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i32; 16]); + _mm512_mask_compressstoreu_epi32(r.as_mut_ptr() as *mut _, 0b1111000011001010, a); + assert_eq!(&r, &[2, 4, 7, 8, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0]); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_mask_compressstoreu_epi32() { + let a = _mm256_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8); + let mut r = [0_i32; 8]; + _mm256_mask_compressstoreu_epi32(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i32; 8]); + _mm256_mask_compressstoreu_epi32(r.as_mut_ptr() as *mut _, 0b11001010, a); + assert_eq!(&r, &[2, 4, 7, 8, 0, 0, 0, 0]); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_mask_compressstoreu_epi32() { + let a = _mm_setr_epi32(1, 2, 3, 4); + let mut r = [0_i32; 4]; + _mm_mask_compressstoreu_epi32(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i32; 4]); + _mm_mask_compressstoreu_epi32(r.as_mut_ptr() as *mut _, 0b1011, a); + assert_eq!(&r, &[1, 2, 4, 0]); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_compressstoreu_epi64() { + let a = _mm512_setr_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let mut r = [0_i64; 8]; + _mm512_mask_compressstoreu_epi64(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i64; 8]); + _mm512_mask_compressstoreu_epi64(r.as_mut_ptr() as *mut _, 0b11001010, a); + assert_eq!(&r, &[2, 4, 7, 8, 0, 0, 0, 0]); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_mask_compressstoreu_epi64() { + let a = _mm256_setr_epi64x(1, 2, 3, 4); + let mut r = [0_i64; 4]; + _mm256_mask_compressstoreu_epi64(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i64; 4]); + _mm256_mask_compressstoreu_epi64(r.as_mut_ptr() as *mut _, 0b1011, a); + assert_eq!(&r, &[1, 2, 4, 0]); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_mask_compressstoreu_epi64() { + let a = _mm_setr_epi64x(1, 2); + let mut r = [0_i64; 2]; + _mm_mask_compressstoreu_epi64(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i64; 2]); + _mm_mask_compressstoreu_epi64(r.as_mut_ptr() as *mut _, 0b10, a); + assert_eq!(&r, &[2, 0]); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_compressstoreu_ps() { + let a = _mm512_setr_ps( + 1_f32, 2_f32, 3_f32, 4_f32, 5_f32, 6_f32, 7_f32, 8_f32, 9_f32, 10_f32, 11_f32, 12_f32, + 13_f32, 14_f32, 15_f32, 16_f32, + ); + let mut r = [0_f32; 16]; + _mm512_mask_compressstoreu_ps(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_f32; 16]); + _mm512_mask_compressstoreu_ps(r.as_mut_ptr() as *mut _, 0b1111000011001010, a); + assert_eq!( + &r, + &[ + 2_f32, 4_f32, 7_f32, 8_f32, 13_f32, 14_f32, 15_f32, 16_f32, 0_f32, 0_f32, 0_f32, + 0_f32, 0_f32, 0_f32, 0_f32, 0_f32 + ] + ); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_mask_compressstoreu_ps() { + let a = _mm256_setr_ps(1_f32, 2_f32, 3_f32, 4_f32, 5_f32, 6_f32, 7_f32, 8_f32); + let mut r = [0_f32; 8]; + _mm256_mask_compressstoreu_ps(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_f32; 8]); + _mm256_mask_compressstoreu_ps(r.as_mut_ptr() as *mut _, 0b11001010, a); + assert_eq!( + &r, + &[2_f32, 4_f32, 7_f32, 8_f32, 0_f32, 0_f32, 0_f32, 0_f32] + ); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_mask_compressstoreu_ps() { + let a = _mm_setr_ps(1_f32, 2_f32, 3_f32, 4_f32); + let mut r = [0.; 4]; + _mm_mask_compressstoreu_ps(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0.; 4]); + _mm_mask_compressstoreu_ps(r.as_mut_ptr() as *mut _, 0b1011, a); + assert_eq!(&r, &[1_f32, 2_f32, 4_f32, 0_f32]); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_compressstoreu_pd() { + let a = _mm512_setr_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let mut r = [0.; 8]; + _mm512_mask_compressstoreu_pd(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0.; 8]); + _mm512_mask_compressstoreu_pd(r.as_mut_ptr() as *mut _, 0b11001010, a); + assert_eq!(&r, &[2., 4., 7., 8., 0., 0., 0., 0.]); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_mask_compressstoreu_pd() { + let a = _mm256_setr_pd(1., 2., 3., 4.); + let mut r = [0.; 4]; + _mm256_mask_compressstoreu_pd(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0.; 4]); + _mm256_mask_compressstoreu_pd(r.as_mut_ptr() as *mut _, 0b1011, a); + assert_eq!(&r, &[1., 2., 4., 0.]); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_mask_compressstoreu_pd() { + let a = _mm_setr_pd(1., 2.); + let mut r = [0.; 2]; + _mm_mask_compressstoreu_pd(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0.; 2]); + _mm_mask_compressstoreu_pd(r.as_mut_ptr() as *mut _, 0b10, a); + assert_eq!(&r, &[2., 0.]); + } + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_mask_expand_epi32() { let src = _mm512_set1_epi32(200); @@ -54843,4 +55567,264 @@ mod tests { let e = _mm512_setr_pd(4., 3., 8., 7., 0., 0., 0., 0.); assert_eq_m512d(r, e); } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_expandloadu_epi32() { + let src = _mm512_set1_epi32(42); + let a = &[1_i32, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let p = a.as_ptr(); + let m = 0b11101000_11001010; + let r = _mm512_mask_expandloadu_epi32(src, m, black_box(p)); + let e = _mm512_set_epi32(8, 7, 6, 42, 5, 42, 42, 42, 4, 3, 42, 42, 2, 42, 1, 42); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_expandloadu_epi32() { + let a = &[1_i32, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let p = a.as_ptr(); + let m = 0b11101000_11001010; + let r = _mm512_maskz_expandloadu_epi32(m, black_box(p)); + let e = _mm512_set_epi32(8, 7, 6, 0, 5, 0, 0, 0, 4, 3, 0, 0, 2, 0, 1, 0); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_mask_expandloadu_epi32() { + let src = _mm256_set1_epi32(42); + let a = &[1_i32, 2, 3, 4, 5, 6, 7, 8]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm256_mask_expandloadu_epi32(src, m, black_box(p)); + let e = _mm256_set_epi32(4, 3, 2, 42, 1, 42, 42, 42); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_maskz_expandloadu_epi32() { + let a = &[1_i32, 2, 3, 4, 5, 6, 7, 8]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm256_maskz_expandloadu_epi32(m, black_box(p)); + let e = _mm256_set_epi32(4, 3, 2, 0, 1, 0, 0, 0); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_mask_expandloadu_epi32() { + let src = _mm_set1_epi32(42); + let a = &[1_i32, 2, 3, 4]; + let p = a.as_ptr(); + let m = 0b11111000; + let r = _mm_mask_expandloadu_epi32(src, m, black_box(p)); + let e = _mm_set_epi32(1, 42, 42, 42); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_maskz_expandloadu_epi32() { + let a = &[1_i32, 2, 3, 4]; + let p = a.as_ptr(); + let m = 0b11111000; + let r = _mm_maskz_expandloadu_epi32(m, black_box(p)); + let e = _mm_set_epi32(1, 0, 0, 0); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_expandloadu_epi64() { + let src = _mm512_set1_epi64(42); + let a = &[1_i64, 2, 3, 4, 5, 6, 7, 8]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm512_mask_expandloadu_epi64(src, m, black_box(p)); + let e = _mm512_set_epi64(4, 3, 2, 42, 1, 42, 42, 42); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_expandloadu_epi64() { + let a = &[1_i64, 2, 3, 4, 5, 6, 7, 8]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm512_maskz_expandloadu_epi64(m, black_box(p)); + let e = _mm512_set_epi64(4, 3, 2, 0, 1, 0, 0, 0); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_mask_expandloadu_epi64() { + let src = _mm256_set1_epi64x(42); + let a = &[1_i64, 2, 3, 4]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm256_mask_expandloadu_epi64(src, m, black_box(p)); + let e = _mm256_set_epi64x(1, 42, 42, 42); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_maskz_expandloadu_epi64() { + let a = &[1_i64, 2, 3, 4]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm256_maskz_expandloadu_epi64(m, black_box(p)); + let e = _mm256_set_epi64x(1, 0, 0, 0); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_mask_expandloadu_epi64() { + let src = _mm_set1_epi64x(42); + let a = &[1_i64, 2]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm_mask_expandloadu_epi64(src, m, black_box(p)); + let e = _mm_set_epi64x(42, 42); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_maskz_expandloadu_epi64() { + let a = &[1_i64, 2]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm_maskz_expandloadu_epi64(m, black_box(p)); + let e = _mm_set_epi64x(0, 0); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_expandloadu_ps() { + let src = _mm512_set1_ps(42.); + let a = &[ + 1.0f32, 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., + ]; + let p = a.as_ptr(); + let m = 0b11101000_11001010; + let r = _mm512_mask_expandloadu_ps(src, m, black_box(p)); + let e = _mm512_set_ps( + 8., 7., 6., 42., 5., 42., 42., 42., 4., 3., 42., 42., 2., 42., 1., 42., + ); + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_expandloadu_ps() { + let a = &[ + 1.0f32, 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., + ]; + let p = a.as_ptr(); + let m = 0b11101000_11001010; + let r = _mm512_maskz_expandloadu_ps(m, black_box(p)); + let e = _mm512_set_ps( + 8., 7., 6., 0., 5., 0., 0., 0., 4., 3., 0., 0., 2., 0., 1., 0., + ); + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_mask_expandloadu_ps() { + let src = _mm256_set1_ps(42.); + let a = &[1.0f32, 2., 3., 4., 5., 6., 7., 8.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm256_mask_expandloadu_ps(src, m, black_box(p)); + let e = _mm256_set_ps(4., 3., 2., 42., 1., 42., 42., 42.); + assert_eq_m256(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_maskz_expandloadu_ps() { + let a = &[1.0f32, 2., 3., 4., 5., 6., 7., 8.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm256_maskz_expandloadu_ps(m, black_box(p)); + let e = _mm256_set_ps(4., 3., 2., 0., 1., 0., 0., 0.); + assert_eq_m256(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_mask_expandloadu_ps() { + let src = _mm_set1_ps(42.); + let a = &[1.0f32, 2., 3., 4.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm_mask_expandloadu_ps(src, m, black_box(p)); + let e = _mm_set_ps(1., 42., 42., 42.); + assert_eq_m128(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_maskz_expandloadu_ps() { + let a = &[1.0f32, 2., 3., 4.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm_maskz_expandloadu_ps(m, black_box(p)); + let e = _mm_set_ps(1., 0., 0., 0.); + assert_eq_m128(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_expandloadu_pd() { + let src = _mm512_set1_pd(42.); + let a = &[1.0f64, 2., 3., 4., 5., 6., 7., 8.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm512_mask_expandloadu_pd(src, m, black_box(p)); + let e = _mm512_set_pd(4., 3., 2., 42., 1., 42., 42., 42.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_expandloadu_pd() { + let a = &[1.0f64, 2., 3., 4., 5., 6., 7., 8.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm512_maskz_expandloadu_pd(m, black_box(p)); + let e = _mm512_set_pd(4., 3., 2., 0., 1., 0., 0., 0.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_mask_expandloadu_pd() { + let src = _mm256_set1_pd(42.); + let a = &[1.0f64, 2., 3., 4.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm256_mask_expandloadu_pd(src, m, black_box(p)); + let e = _mm256_set_pd(1., 42., 42., 42.); + assert_eq_m256d(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm256_maskz_expandloadu_pd() { + let a = &[1.0f64, 2., 3., 4.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm256_maskz_expandloadu_pd(m, black_box(p)); + let e = _mm256_set_pd(1., 0., 0., 0.); + assert_eq_m256d(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_mask_expandloadu_pd() { + let src = _mm_set1_pd(42.); + let a = &[1.0f64, 2.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm_mask_expandloadu_pd(src, m, black_box(p)); + let e = _mm_set_pd(42., 42.); + assert_eq_m128d(r, e); + } + + #[simd_test(enable = "avx512f,avx512vl")] + unsafe fn test_mm_maskz_expandloadu_pd() { + let a = &[1.0f64, 2.]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm_maskz_expandloadu_pd(m, black_box(p)); + let e = _mm_set_pd(0., 0.); + assert_eq_m128d(r, e); + } } diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs b/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs index b7a385dd97..1c81840ba7 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs @@ -1,8 +1,299 @@ -use crate::core_arch::{simd::*, simd_llvm::*, x86::*}; +use crate::{ + arch::asm, + core_arch::{simd::*, simd_llvm::*, x86::*}, +}; #[cfg(test)] use stdarch_test::assert_instr; +/// Load contiguous active 16-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_expandloadu_epi16) +#[inline] +#[target_feature(enable = "avx512f,avx512bw,avx512vbmi2")] +pub unsafe fn _mm512_mask_expandloadu_epi16( + src: __m512i, + k: __mmask32, + mem_addr: *const i16, +) -> __m512i { + let mut dst: __m512i = src; + asm!( + vpl!("vpexpandw {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 16-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_expandloadu_epi16) +#[inline] +#[target_feature(enable = "avx512f,avx512bw,avx512vbmi2")] +pub unsafe fn _mm512_maskz_expandloadu_epi16(k: __mmask32, mem_addr: *const i16) -> __m512i { + let mut dst: __m512i; + asm!( + vpl!("vpexpandw {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 16-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_expandloadu_epi16) +#[inline] +#[target_feature(enable = "avx512f,avx512vbmi2,avx512vl,avx")] +pub unsafe fn _mm256_mask_expandloadu_epi16( + src: __m256i, + k: __mmask16, + mem_addr: *const i16, +) -> __m256i { + let mut dst: __m256i = src; + asm!( + vpl!("vpexpandw {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 16-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_maskz_expandloadu_epi16) +#[inline] +#[target_feature(enable = "avx512f,avx512vbmi2,avx512vl,avx")] +pub unsafe fn _mm256_maskz_expandloadu_epi16(k: __mmask16, mem_addr: *const i16) -> __m256i { + let mut dst: __m256i; + asm!( + vpl!("vpexpandw {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 16-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_expandloadu_epi16) +#[inline] +#[target_feature(enable = "avx512f,avx512vbmi2,avx512vl,avx,sse")] +pub unsafe fn _mm_mask_expandloadu_epi16( + src: __m128i, + k: __mmask8, + mem_addr: *const i16, +) -> __m128i { + let mut dst: __m128i = src; + asm!( + vpl!("vpexpandw {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 16-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maskz_expandloadu_epi16) +#[inline] +#[target_feature(enable = "avx512f,avx512vbmi2,avx512vl,avx,sse")] +pub unsafe fn _mm_maskz_expandloadu_epi16(k: __mmask8, mem_addr: *const i16) -> __m128i { + let mut dst: __m128i; + asm!( + vpl!("vpexpandw {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 8-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_expandloadu_epi8) +#[inline] +#[target_feature(enable = "avx512f,avx512bw,avx512vbmi2")] +pub unsafe fn _mm512_mask_expandloadu_epi8( + src: __m512i, + k: __mmask64, + mem_addr: *const i8, +) -> __m512i { + let mut dst: __m512i = src; + asm!( + vpl!("vpexpandb {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 8-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_expandloadu_epi8) +#[inline] +#[target_feature(enable = "avx512f,avx512bw,avx512vbmi2")] +pub unsafe fn _mm512_maskz_expandloadu_epi8(k: __mmask64, mem_addr: *const i8) -> __m512i { + let mut dst: __m512i; + asm!( + vpl!("vpexpandb {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(zmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 8-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_expandloadu_epi8) +#[inline] +#[target_feature(enable = "avx512f,avx512bw,avx512vbmi2,avx512vl,avx")] +pub unsafe fn _mm256_mask_expandloadu_epi8( + src: __m256i, + k: __mmask32, + mem_addr: *const i8, +) -> __m256i { + let mut dst: __m256i = src; + asm!( + vpl!("vpexpandb {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 8-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_maskz_expandloadu_epi8) +#[inline] +#[target_feature(enable = "avx512f,avx512bw,avx512vbmi2,avx512vl,avx")] +pub unsafe fn _mm256_maskz_expandloadu_epi8(k: __mmask32, mem_addr: *const i8) -> __m256i { + let mut dst: __m256i; + asm!( + vpl!("vpexpandb {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(ymm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 8-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_expandloadu_epi8) +#[inline] +#[target_feature(enable = "avx512f,avx512vbmi2,avx512vl,avx,sse")] +pub unsafe fn _mm_mask_expandloadu_epi8( + src: __m128i, + k: __mmask16, + mem_addr: *const i8, +) -> __m128i { + let mut dst: __m128i = src; + asm!( + vpl!("vpexpandb {dst}{{{k}}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = inout(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Load contiguous active 8-bit integers from unaligned memory at mem_addr (those with their respective bit set in mask k), and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maskz_expandloadu_epi8) +#[inline] +#[target_feature(enable = "avx512f,avx512vbmi2,avx512vl,avx,sse")] +pub unsafe fn _mm_maskz_expandloadu_epi8(k: __mmask16, mem_addr: *const i8) -> __m128i { + let mut dst: __m128i; + asm!( + vpl!("vpexpandb {dst}{{{k}}} {{z}}"), + p = in(reg) mem_addr, + k = in(kreg) k, + dst = out(xmm_reg) dst, + options(pure, readonly, nostack) + ); + dst +} + +/// Contiguously store the active 16-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_compressstoreu_epi16) +#[inline] +#[target_feature(enable = "avx512vbmi2")] +#[cfg_attr(test, assert_instr(vpcompressw))] +pub unsafe fn _mm512_mask_compressstoreu_epi16(base_addr: *mut u8, k: __mmask32, a: __m512i) { + vcompressstorew(base_addr as *mut _, a.as_i16x32(), k) +} + +/// Contiguously store the active 16-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_compressstoreu_epi16) +#[inline] +#[target_feature(enable = "avx512vbmi2,avx512vl")] +#[cfg_attr(test, assert_instr(vpcompressw))] +pub unsafe fn _mm256_mask_compressstoreu_epi16(base_addr: *mut u8, k: __mmask16, a: __m256i) { + vcompressstorew256(base_addr as *mut _, a.as_i16x16(), k) +} + +/// Contiguously store the active 16-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_compressstoreu_epi16) +#[inline] +#[target_feature(enable = "avx512vbmi2,avx512vl")] +#[cfg_attr(test, assert_instr(vpcompressw))] +pub unsafe fn _mm_mask_compressstoreu_epi16(base_addr: *mut u8, k: __mmask8, a: __m128i) { + vcompressstorew128(base_addr as *mut _, a.as_i16x8(), k) +} + +/// Contiguously store the active 8-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_compressstoreu_epi8) +#[inline] +#[target_feature(enable = "avx512vbmi2")] +#[cfg_attr(test, assert_instr(vpcompressb))] +pub unsafe fn _mm512_mask_compressstoreu_epi8(base_addr: *mut u8, k: __mmask64, a: __m512i) { + vcompressstoreb(base_addr as *mut _, a.as_i8x64(), k) +} + +/// Contiguously store the active 8-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm256_mask_compressstoreu_epi8) +#[inline] +#[target_feature(enable = "avx512vbmi2,avx512vl")] +#[cfg_attr(test, assert_instr(vpcompressb))] +pub unsafe fn _mm256_mask_compressstoreu_epi8(base_addr: *mut u8, k: __mmask32, a: __m256i) { + vcompressstoreb256(base_addr as *mut _, a.as_i8x32(), k) +} + +/// Contiguously store the active 8-bit integers in a (those with their respective bit set in writemask k) to unaligned memory at base_addr. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_compressstoreu_epi8) +#[inline] +#[target_feature(enable = "avx512vbmi2,avx512vl")] +#[cfg_attr(test, assert_instr(vpcompressb))] +pub unsafe fn _mm_mask_compressstoreu_epi8(base_addr: *mut u8, k: __mmask16, a: __m128i) { + vcompressstoreb128(base_addr as *mut _, a.as_i8x16(), k) +} + /// Contiguously store the active 16-bit integers in a (those with their respective bit set in writemask k) to dst, and pass through the remaining elements from src. /// /// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_compress_epi16&expand=1192) @@ -1990,6 +2281,20 @@ pub unsafe fn _mm_maskz_shrdi_epi16( #[allow(improper_ctypes)] extern "C" { + #[link_name = "llvm.x86.avx512.mask.compress.store.w.512"] + fn vcompressstorew(mem: *mut i8, data: i16x32, mask: u32); + #[link_name = "llvm.x86.avx512.mask.compress.store.w.256"] + fn vcompressstorew256(mem: *mut i8, data: i16x16, mask: u16); + #[link_name = "llvm.x86.avx512.mask.compress.store.w.128"] + fn vcompressstorew128(mem: *mut i8, data: i16x8, mask: u8); + + #[link_name = "llvm.x86.avx512.mask.compress.store.b.512"] + fn vcompressstoreb(mem: *mut i8, data: i8x64, mask: u64); + #[link_name = "llvm.x86.avx512.mask.compress.store.b.256"] + fn vcompressstoreb256(mem: *mut i8, data: i8x32, mask: u32); + #[link_name = "llvm.x86.avx512.mask.compress.store.b.128"] + fn vcompressstoreb128(mem: *mut i8, data: i8x16, mask: u16); + #[link_name = "llvm.x86.avx512.mask.compress.w.512"] fn vpcompressw(a: i16x32, src: i16x32, mask: u32) -> i16x32; #[link_name = "llvm.x86.avx512.mask.compress.w.256"] @@ -2063,6 +2368,7 @@ mod tests { use stdarch_test::simd_test; use crate::core_arch::x86::*; + use crate::hint::black_box; #[simd_test(enable = "avx512vbmi2")] unsafe fn test_mm512_mask_compress_epi16() { @@ -3545,4 +3851,271 @@ mod tests { let e = _mm_set1_epi16(1); assert_eq_m128i(r, e); } + + #[simd_test(enable = "avx512vbmi2")] + unsafe fn test_mm512_mask_expandloadu_epi16() { + let src = _mm512_set1_epi16(42); + let a = &[ + 1_i16, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, + ]; + let p = a.as_ptr(); + let m = 0b11101000_11001010_11110000_00001111; + let r = _mm512_mask_expandloadu_epi16(src, m, black_box(p)); + let e = _mm512_set_epi16( + 16, 15, 14, 42, 13, 42, 42, 42, 12, 11, 42, 42, 10, 42, 9, 42, 8, 7, 6, 5, 42, 42, 42, + 42, 42, 42, 42, 42, 4, 3, 2, 1, + ); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512vbmi2")] + unsafe fn test_mm512_maskz_expandloadu_epi16() { + let a = &[ + 1_i16, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, + ]; + let p = a.as_ptr(); + let m = 0b11101000_11001010_11110000_00001111; + let r = _mm512_maskz_expandloadu_epi16(m, black_box(p)); + let e = _mm512_set_epi16( + 16, 15, 14, 0, 13, 0, 0, 0, 12, 11, 0, 0, 10, 0, 9, 0, 8, 7, 6, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 3, 2, 1, + ); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm256_mask_expandloadu_epi16() { + let src = _mm256_set1_epi16(42); + let a = &[1_i16, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let p = a.as_ptr(); + let m = 0b11101000_11001010; + let r = _mm256_mask_expandloadu_epi16(src, m, black_box(p)); + let e = _mm256_set_epi16(8, 7, 6, 42, 5, 42, 42, 42, 4, 3, 42, 42, 2, 42, 1, 42); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm256_maskz_expandloadu_epi16() { + let a = &[1_i16, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let p = a.as_ptr(); + let m = 0b11101000_11001010; + let r = _mm256_maskz_expandloadu_epi16(m, black_box(p)); + let e = _mm256_set_epi16(8, 7, 6, 0, 5, 0, 0, 0, 4, 3, 0, 0, 2, 0, 1, 0); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm_mask_expandloadu_epi16() { + let src = _mm_set1_epi16(42); + let a = &[1_i16, 2, 3, 4, 5, 6, 7, 8]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm_mask_expandloadu_epi16(src, m, black_box(p)); + let e = _mm_set_epi16(4, 3, 2, 42, 1, 42, 42, 42); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm_maskz_expandloadu_epi16() { + let a = &[1_i16, 2, 3, 4, 5, 6, 7, 8]; + let p = a.as_ptr(); + let m = 0b11101000; + let r = _mm_maskz_expandloadu_epi16(m, black_box(p)); + let e = _mm_set_epi16(4, 3, 2, 0, 1, 0, 0, 0); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512vbmi2")] + unsafe fn test_mm512_mask_expandloadu_epi8() { + let src = _mm512_set1_epi8(42); + let a = &[ + 1_i8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + ]; + let p = a.as_ptr(); + let m = 0b11101000_11001010_11110000_00001111_11111111_00000000_10101010_01010101; + let r = _mm512_mask_expandloadu_epi8(src, m, black_box(p)); + let e = _mm512_set_epi8( + 32, 31, 30, 42, 29, 42, 42, 42, 28, 27, 42, 42, 26, 42, 25, 42, 24, 23, 22, 21, 42, 42, + 42, 42, 42, 42, 42, 42, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 42, 42, 42, 42, + 42, 42, 42, 42, 8, 42, 7, 42, 6, 42, 5, 42, 42, 4, 42, 3, 42, 2, 42, 1, + ); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512vbmi2")] + unsafe fn test_mm512_maskz_expandloadu_epi8() { + let a = &[ + 1_i8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + ]; + let p = a.as_ptr(); + let m = 0b11101000_11001010_11110000_00001111_11111111_00000000_10101010_01010101; + let r = _mm512_maskz_expandloadu_epi8(m, black_box(p)); + let e = _mm512_set_epi8( + 32, 31, 30, 0, 29, 0, 0, 0, 28, 27, 0, 0, 26, 0, 25, 0, 24, 23, 22, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, + 7, 0, 6, 0, 5, 0, 0, 4, 0, 3, 0, 2, 0, 1, + ); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm256_mask_expandloadu_epi8() { + let src = _mm256_set1_epi8(42); + let a = &[ + 1_i8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, + ]; + let p = a.as_ptr(); + let m = 0b11101000_11001010_11110000_00001111; + let r = _mm256_mask_expandloadu_epi8(src, m, black_box(p)); + let e = _mm256_set_epi8( + 16, 15, 14, 42, 13, 42, 42, 42, 12, 11, 42, 42, 10, 42, 9, 42, 8, 7, 6, 5, 42, 42, 42, + 42, 42, 42, 42, 42, 4, 3, 2, 1, + ); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm256_maskz_expandloadu_epi8() { + let a = &[ + 1_i8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, + ]; + let p = a.as_ptr(); + let m = 0b11101000_11001010_11110000_00001111; + let r = _mm256_maskz_expandloadu_epi8(m, black_box(p)); + let e = _mm256_set_epi8( + 16, 15, 14, 0, 13, 0, 0, 0, 12, 11, 0, 0, 10, 0, 9, 0, 8, 7, 6, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 3, 2, 1, + ); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm_mask_expandloadu_epi8() { + let src = _mm_set1_epi8(42); + let a = &[1_i8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let p = a.as_ptr(); + let m = 0b11101000_11001010; + let r = _mm_mask_expandloadu_epi8(src, m, black_box(p)); + let e = _mm_set_epi8(8, 7, 6, 42, 5, 42, 42, 42, 4, 3, 42, 42, 2, 42, 1, 42); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm_maskz_expandloadu_epi8() { + let a = &[1_i8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let p = a.as_ptr(); + let m = 0b11101000_11001010; + let r = _mm_maskz_expandloadu_epi8(m, black_box(p)); + let e = _mm_set_epi8(8, 7, 6, 0, 5, 0, 0, 0, 4, 3, 0, 0, 2, 0, 1, 0); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512vbmi2")] + unsafe fn test_mm512_mask_compressstoreu_epi16() { + let a = _mm512_set_epi16( + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + ); + let mut r = [0_i16; 32]; + _mm512_mask_compressstoreu_epi16(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i16; 32]); + _mm512_mask_compressstoreu_epi16( + r.as_mut_ptr() as *mut _, + 0b11110000_11001010_11111111_00000000, + a, + ); + assert_eq!( + &r, + &[ + 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 23, 24, 29, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm256_mask_compressstoreu_epi16() { + let a = _mm256_set_epi16(16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + let mut r = [0_i16; 16]; + _mm256_mask_compressstoreu_epi16(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i16; 16]); + _mm256_mask_compressstoreu_epi16(r.as_mut_ptr() as *mut _, 0b11110000_11001010, a); + assert_eq!(&r, &[2, 4, 7, 8, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0]); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm_mask_compressstoreu_epi16() { + let a = _mm_set_epi16(8, 7, 6, 5, 4, 3, 2, 1); + let mut r = [0_i16; 8]; + _mm_mask_compressstoreu_epi16(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i16; 8]); + _mm_mask_compressstoreu_epi16(r.as_mut_ptr() as *mut _, 0b11110000, a); + assert_eq!(&r, &[5, 6, 7, 8, 0, 0, 0, 0]); + } + + #[simd_test(enable = "avx512vbmi2")] + unsafe fn test_mm512_mask_compressstoreu_epi8() { + let a = _mm512_set_epi8( + 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, + 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + ); + let mut r = [0_i8; 64]; + _mm512_mask_compressstoreu_epi8(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i8; 64]); + _mm512_mask_compressstoreu_epi8( + r.as_mut_ptr() as *mut _, + 0b11110000_11001010_11111111_00000000_10101010_01010101_11110000_00001111, + a, + ); + assert_eq!( + &r, + &[ + 1, 2, 3, 4, 13, 14, 15, 16, 17, 19, 21, 23, 26, 28, 30, 32, 41, 42, 43, 44, 45, 46, + 47, 48, 50, 52, 55, 56, 61, 62, 63, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm256_mask_compressstoreu_epi8() { + let a = _mm256_set_epi8( + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + ); + let mut r = [0_i8; 32]; + _mm256_mask_compressstoreu_epi8(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i8; 32]); + _mm256_mask_compressstoreu_epi8( + r.as_mut_ptr() as *mut _, + 0b11110000_11001010_11111111_00000000, + a, + ); + assert_eq!( + &r, + &[ + 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 23, 24, 29, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + } + + #[simd_test(enable = "avx512vbmi2,avx512vl")] + unsafe fn test_mm_mask_compressstoreu_epi8() { + let a = _mm_set_epi8(16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + let mut r = [0_i8; 16]; + _mm_mask_compressstoreu_epi8(r.as_mut_ptr() as *mut _, 0, a); + assert_eq!(&r, &[0_i8; 16]); + _mm_mask_compressstoreu_epi8(r.as_mut_ptr() as *mut _, 0b11110000_11001010, a); + assert_eq!(&r, &[2, 4, 7, 8, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0]); + } } diff --git a/library/stdarch/crates/core_arch/src/x86/sse42.rs b/library/stdarch/crates/core_arch/src/x86/sse42.rs index fb3a22b519..f474b0671d 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse42.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse42.rs @@ -520,7 +520,7 @@ pub unsafe fn _mm_cmpestra(a: __m128i, la: i32, b: __m128i, lb: } /// Starting with the initial value in `crc`, return the accumulated -/// CRC32 value for unsigned 8-bit integer `v`. +/// CRC32-C value for unsigned 8-bit integer `v`. /// /// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_crc32_u8) #[inline] @@ -532,7 +532,7 @@ pub unsafe fn _mm_crc32_u8(crc: u32, v: u8) -> u32 { } /// Starting with the initial value in `crc`, return the accumulated -/// CRC32 value for unsigned 16-bit integer `v`. +/// CRC32-C value for unsigned 16-bit integer `v`. /// /// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_crc32_u16) #[inline] @@ -544,7 +544,7 @@ pub unsafe fn _mm_crc32_u16(crc: u32, v: u16) -> u32 { } /// Starting with the initial value in `crc`, return the accumulated -/// CRC32 value for unsigned 32-bit integer `v`. +/// CRC32-C value for unsigned 32-bit integer `v`. /// /// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_crc32_u32) #[inline] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse42.rs b/library/stdarch/crates/core_arch/src/x86_64/sse42.rs index 405073261c..6b5d087c17 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse42.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse42.rs @@ -10,7 +10,7 @@ extern "C" { } /// Starting with the initial value in `crc`, return the accumulated -/// CRC32 value for unsigned 64-bit integer `v`. +/// CRC32-C value for unsigned 64-bit integer `v`. /// /// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_crc32_u64) #[inline] diff --git a/library/stdarch/crates/std_detect/src/detect/arch/aarch64.rs b/library/stdarch/crates/std_detect/src/detect/arch/aarch64.rs index 2f90555428..f32f961ae5 100644 --- a/library/stdarch/crates/std_detect/src/detect/arch/aarch64.rs +++ b/library/stdarch/crates/std_detect/src/detect/arch/aarch64.rs @@ -2,6 +2,7 @@ features! { @TARGET: aarch64; + @CFG: target_arch = "aarch64"; @MACRO_NAME: is_aarch64_feature_detected; @MACRO_ATTRS: /// This macro tests, at runtime, whether an `aarch64` feature is enabled on aarch64 platforms. @@ -31,7 +32,8 @@ features! { /// * `"flagm"` - FEAT_FLAGM /// * `"ssbs"` - FEAT_SSBS /// * `"sb"` - FEAT_SB - /// * `"pauth"` - FEAT_PAuth + /// * `"paca"` - FEAT_PAuth (address authentication) + /// * `"pacg"` - FEAT_Pauth (generic authentication) /// * `"dpb"` - FEAT_DPB /// * `"dpb2"` - FEAT_DPB2 /// * `"sve2"` - FEAT_SVE2 @@ -55,7 +57,7 @@ features! { /// * `"sm4"` - FEAT_SM3 & FEAT_SM4 /// /// [docs]: https://developer.arm.com/documentation/ddi0487/latest - #[unstable(feature = "stdsimd", issue = "27731")] + #[stable(feature = "simd_aarch64", since = "1.60.0")] @BIND_FEATURE_NAME: "asimd"; "neon"; @NO_RUNTIME_DETECTION: "ras"; @NO_RUNTIME_DETECTION: "v8.1a"; @@ -65,84 +67,86 @@ features! { @NO_RUNTIME_DETECTION: "v8.5a"; @NO_RUNTIME_DETECTION: "v8.6a"; @NO_RUNTIME_DETECTION: "v8.7a"; - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] asimd: "neon"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] asimd: "neon"; /// FEAT_AdvSIMD (Advanced SIMD/NEON) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] pmull: "pmull"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pmull: "pmull"; /// FEAT_PMULL (Polynomial Multiply) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] fp: "fp"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp: "fp"; /// FEAT_FP (Floating point support) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] fp16: "fp16"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp16: "fp16"; /// FEAT_FP16 (Half-float support) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve: "sve"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve: "sve"; /// FEAT_SVE (Scalable Vector Extension) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] crc: "crc"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] crc: "crc"; /// FEAT_CRC32 (Cyclic Redundancy Check) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] lse: "lse"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse: "lse"; /// FEAT_LSE (Large System Extension - atomics) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] lse2: "lse2"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse2: "lse2"; /// FEAT_LSE2 (unaligned and register-pair atomics) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rdm: "rdm"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rdm: "rdm"; /// FEAT_RDM (Rounding Doubling Multiply - ASIMDRDM) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rcpc: "rcpc"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc: "rcpc"; /// FEAT_LRCPC (Release consistent Processor consistent) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rcpc2: "rcpc2"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc2: "rcpc2"; /// FEAT_LRCPC2 (RCPC with immediate offsets) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] dotprod: "dotprod"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dotprod: "dotprod"; /// FEAT_DotProd (Vector Dot-Product - ASIMDDP) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] tme: "tme"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] tme: "tme"; /// FEAT_TME (Transactional Memory Extensions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] fhm: "fhm"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fhm: "fhm"; /// FEAT_FHM (fp16 multiplication instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] dit: "dit"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dit: "dit"; /// FEAT_DIT (Data Independent Timing instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] flagm: "flagm"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] flagm: "flagm"; /// FEAT_FLAGM (flag manipulation instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] ssbs: "ssbs"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] ssbs: "ssbs"; /// FEAT_SSBS (speculative store bypass safe) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sb: "sb"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sb: "sb"; /// FEAT_SB (speculation barrier) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] pauth: "pauth"; - /// FEAT_PAuth (pointer authentication) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] dpb: "dpb"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] paca: "paca"; + /// FEAT_PAuth (address authentication) + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pacg: "pacg"; + /// FEAT_PAuth (generic authentication) + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb: "dpb"; /// FEAT_DPB (aka dcpop - data cache clean to point of persistence) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] dpb2: "dpb2"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb2: "dpb2"; /// FEAT_DPB2 (aka dcpodp - data cache clean to point of deep persistence) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2: "sve2"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2: "sve2"; /// FEAT_SVE2 (Scalable Vector Extension 2) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2_aes: "sve2-aes"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_aes: "sve2-aes"; /// FEAT_SVE_AES (SVE2 AES crypto) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2_sm4: "sve2-sm4"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sm4: "sve2-sm4"; /// FEAT_SVE_SM4 (SVE2 SM4 crypto) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2_sha3: "sve2-sha3"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sha3: "sve2-sha3"; /// FEAT_SVE_SHA3 (SVE2 SHA3 crypto) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2_bitperm: "sve2-bitperm"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_bitperm: "sve2-bitperm"; /// FEAT_SVE_BitPerm (SVE2 bit permutation instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] frintts: "frintts"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] frintts: "frintts"; /// FEAT_FRINTTS (float to integer rounding instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] i8mm: "i8mm"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] i8mm: "i8mm"; /// FEAT_I8MM (integer matrix multiplication, plus ASIMD support) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] f32mm: "f32mm"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f32mm: "f32mm"; /// FEAT_F32MM (single-precision matrix multiplication) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] f64mm: "f64mm"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f64mm: "f64mm"; /// FEAT_F64MM (double-precision matrix multiplication) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] bf16: "bf16"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bf16: "bf16"; /// FEAT_BF16 (BFloat16 type, plus MM instructions, plus ASIMD support) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rand: "rand"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rand: "rand"; /// FEAT_RNG (Random Number Generator) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] bti: "bti"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bti: "bti"; /// FEAT_BTI (Branch Target Identification) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] mte: "mte"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] mte: "mte"; /// FEAT_MTE (Memory Tagging Extension) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] jsconv: "jsconv"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] jsconv: "jsconv"; /// FEAT_JSCVT (JavaScript float conversion instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] fcma: "fcma"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fcma: "fcma"; /// FEAT_FCMA (float complex number operations) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] aes: "aes"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] aes: "aes"; /// FEAT_AES (AES instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sha2: "sha2"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha2: "sha2"; /// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sha3: "sha3"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha3: "sha3"; /// FEAT_SHA512 & FEAT_SHA3 (SHA2-512 & SHA3 instructions) - @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sm4: "sm4"; + @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sm4: "sm4"; /// FEAT_SM3 & FEAT_SM4 (SM3 & SM4 instructions) } diff --git a/library/stdarch/crates/std_detect/src/detect/arch/arm.rs b/library/stdarch/crates/std_detect/src/detect/arch/arm.rs index 9e7dda094f..897dc314c7 100644 --- a/library/stdarch/crates/std_detect/src/detect/arch/arm.rs +++ b/library/stdarch/crates/std_detect/src/detect/arch/arm.rs @@ -2,6 +2,7 @@ features! { @TARGET: arm; + @CFG: target_arch = "arm"; @MACRO_NAME: is_arm_feature_detected; @MACRO_ATTRS: /// Checks if `arm` feature is enabled. diff --git a/library/stdarch/crates/std_detect/src/detect/arch/mips.rs b/library/stdarch/crates/std_detect/src/detect/arch/mips.rs index ada81b83ec..ae27d0093c 100644 --- a/library/stdarch/crates/std_detect/src/detect/arch/mips.rs +++ b/library/stdarch/crates/std_detect/src/detect/arch/mips.rs @@ -2,6 +2,7 @@ features! { @TARGET: mips; + @CFG: target_arch = "mips"; @MACRO_NAME: is_mips_feature_detected; @MACRO_ATTRS: /// Checks if `mips` feature is enabled. diff --git a/library/stdarch/crates/std_detect/src/detect/arch/mips64.rs b/library/stdarch/crates/std_detect/src/detect/arch/mips64.rs index 6a0bb159ba..7182ec2da4 100644 --- a/library/stdarch/crates/std_detect/src/detect/arch/mips64.rs +++ b/library/stdarch/crates/std_detect/src/detect/arch/mips64.rs @@ -2,6 +2,7 @@ features! { @TARGET: mips64; + @CFG: target_arch = "mips64"; @MACRO_NAME: is_mips64_feature_detected; @MACRO_ATTRS: /// Checks if `mips64` feature is enabled. diff --git a/library/stdarch/crates/std_detect/src/detect/arch/mod.rs b/library/stdarch/crates/std_detect/src/detect/arch/mod.rs new file mode 100644 index 0000000000..81a1f23e87 --- /dev/null +++ b/library/stdarch/crates/std_detect/src/detect/arch/mod.rs @@ -0,0 +1,56 @@ +#![allow(dead_code)] + +use cfg_if::cfg_if; + +// Export the macros for all supported architectures. +#[macro_use] +mod x86; +#[macro_use] +mod arm; +#[macro_use] +mod aarch64; +#[macro_use] +mod riscv; +#[macro_use] +mod powerpc; +#[macro_use] +mod powerpc64; +#[macro_use] +mod mips; +#[macro_use] +mod mips64; + +cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + pub use x86::*; + } else if #[cfg(target_arch = "arm")] { + pub use arm::*; + } else if #[cfg(target_arch = "aarch64")] { + pub use aarch64::*; + } else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { + pub use riscv::*; + } else if #[cfg(target_arch = "powerpc")] { + pub use powerpc::*; + } else if #[cfg(target_arch = "powerpc64")] { + pub use powerpc64::*; + } else if #[cfg(target_arch = "mips")] { + pub use mips::*; + } else if #[cfg(target_arch = "mips64")] { + pub use mips64::*; + } else { + // Unimplemented architecture: + #[doc(hidden)] + pub(crate) enum Feature { + Null + } + #[doc(hidden)] + pub mod __is_feature_detected {} + + impl Feature { + #[doc(hidden)] + pub(crate) fn from_str(_s: &str) -> Result { Err(()) } + #[doc(hidden)] + pub(crate) fn to_str(self) -> &'static str { "" } + } + } +} diff --git a/library/stdarch/crates/std_detect/src/detect/arch/powerpc.rs b/library/stdarch/crates/std_detect/src/detect/arch/powerpc.rs index 44bd7f337f..d135cd95de 100644 --- a/library/stdarch/crates/std_detect/src/detect/arch/powerpc.rs +++ b/library/stdarch/crates/std_detect/src/detect/arch/powerpc.rs @@ -2,6 +2,7 @@ features! { @TARGET: powerpc; + @CFG: target_arch = "powerpc"; @MACRO_NAME: is_powerpc_feature_detected; @MACRO_ATTRS: /// Checks if `powerpc` feature is enabled. diff --git a/library/stdarch/crates/std_detect/src/detect/arch/powerpc64.rs b/library/stdarch/crates/std_detect/src/detect/arch/powerpc64.rs index 17e4d958bf..773afd6ceb 100644 --- a/library/stdarch/crates/std_detect/src/detect/arch/powerpc64.rs +++ b/library/stdarch/crates/std_detect/src/detect/arch/powerpc64.rs @@ -2,6 +2,7 @@ features! { @TARGET: powerpc64; + @CFG: target_arch = "powerpc64"; @MACRO_NAME: is_powerpc64_feature_detected; @MACRO_ATTRS: /// Checks if `powerpc` feature is enabled. diff --git a/library/stdarch/crates/std_detect/src/detect/arch/riscv.rs b/library/stdarch/crates/std_detect/src/detect/arch/riscv.rs index ac43b2c4dd..5ea36e7c1c 100644 --- a/library/stdarch/crates/std_detect/src/detect/arch/riscv.rs +++ b/library/stdarch/crates/std_detect/src/detect/arch/riscv.rs @@ -2,6 +2,7 @@ features! { @TARGET: riscv; + @CFG: any(target_arch = "riscv32", target_arch = "riscv64"); @MACRO_NAME: is_riscv_feature_detected; @MACRO_ATTRS: /// A macro to test at *runtime* whether instruction sets are available on diff --git a/library/stdarch/crates/std_detect/src/detect/arch/x86.rs b/library/stdarch/crates/std_detect/src/detect/arch/x86.rs index 22dbb98412..893e1a887f 100644 --- a/library/stdarch/crates/std_detect/src/detect/arch/x86.rs +++ b/library/stdarch/crates/std_detect/src/detect/arch/x86.rs @@ -17,6 +17,7 @@ features! { @TARGET: x86; + @CFG: any(target_arch = "x86", target_arch = "x86_64"); @MACRO_NAME: is_x86_feature_detected; @MACRO_ATTRS: /// A macro to test at *runtime* whether a CPU feature is available on diff --git a/library/stdarch/crates/std_detect/src/detect/error_macros.rs b/library/stdarch/crates/std_detect/src/detect/error_macros.rs deleted file mode 100644 index 1e70da486f..0000000000 --- a/library/stdarch/crates/std_detect/src/detect/error_macros.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! The `is_{target_arch}_feature_detected!` macro are only available on their -//! architecture. These macros provide a better error messages when the user -//! attempts to call them in a different architecture. - -/// Prevents compilation if `is_x86_feature_detected` is used somewhere -/// else than `x86` and `x86_64` targets. -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -#[macro_export] -#[unstable(feature = "stdsimd", issue = "27731")] -macro_rules! is_x86_feature_detected { - ($t: tt) => { - compile_error!( - r#" - is_x86_feature_detected can only be used on x86 and x86_64 targets. - You can prevent it from being used in other architectures by - guarding it behind a cfg(target_arch) as follows: - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - if is_x86_feature_detected(...) { ... } - } - "# - ) - }; -} - -/// Prevents compilation if `is_arm_feature_detected` is used somewhere else -/// than `ARM` targets. -#[cfg(not(target_arch = "arm"))] -#[macro_export] -#[unstable(feature = "stdsimd", issue = "27731")] -macro_rules! is_arm_feature_detected { - ($t:tt) => { - compile_error!( - r#" - is_arm_feature_detected can only be used on ARM targets. - You can prevent it from being used in other architectures by - guarding it behind a cfg(target_arch) as follows: - - #[cfg(target_arch = "arm")] { - if is_arm_feature_detected(...) { ... } - } - "# - ) - }; -} - -/// Prevents compilation if `is_aarch64_feature_detected` is used somewhere else -/// than `aarch64` targets. -#[cfg(not(target_arch = "aarch64"))] -#[macro_export] -#[unstable(feature = "stdsimd", issue = "27731")] -macro_rules! is_aarch64_feature_detected { - ($t: tt) => { - compile_error!( - r#" - is_aarch64_feature_detected can only be used on AArch64 targets. - You can prevent it from being used in other architectures by - guarding it behind a cfg(target_arch) as follows: - - #[cfg(target_arch = "aarch64")] { - if is_aarch64_feature_detected(...) { ... } - } - "# - ) - }; -} - -/// Prevents compilation if `is_riscv_feature_detected` is used somewhere else -/// than `riscv32` or `riscv64` targets. -#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] -#[macro_export] -#[unstable(feature = "stdsimd", issue = "27731")] -macro_rules! is_riscv_feature_detected { - ($t: tt) => { - compile_error!( - r#" - is_riscv_feature_detected can only be used on RISC-V targets. - You can prevent it from being used in other architectures by - guarding it behind a cfg(target_arch) as follows: - - #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { - if is_riscv_feature_detected(...) { ... } - } - "# - ) - }; -} - -/// Prevents compilation if `is_powerpc_feature_detected` is used somewhere else -/// than `PowerPC` targets. -#[cfg(not(target_arch = "powerpc"))] -#[macro_export] -#[unstable(feature = "stdsimd", issue = "27731")] -macro_rules! is_powerpc_feature_detected { - ($t:tt) => { - compile_error!( - r#" -is_powerpc_feature_detected can only be used on PowerPC targets. -You can prevent it from being used in other architectures by -guarding it behind a cfg(target_arch) as follows: - - #[cfg(target_arch = "powerpc")] { - if is_powerpc_feature_detected(...) { ... } - } -"# - ) - }; -} - -/// Prevents compilation if `is_powerpc64_feature_detected` is used somewhere -/// else than `PowerPC64` targets. -#[cfg(not(target_arch = "powerpc64"))] -#[macro_export] -#[unstable(feature = "stdsimd", issue = "27731")] -macro_rules! is_powerpc64_feature_detected { - ($t:tt) => { - compile_error!( - r#" -is_powerpc64_feature_detected can only be used on PowerPC64 targets. -You can prevent it from being used in other architectures by -guarding it behind a cfg(target_arch) as follows: - - #[cfg(target_arch = "powerpc64")] { - if is_powerpc64_feature_detected(...) { ... } - } -"# - ) - }; -} - -/// Prevents compilation if `is_mips_feature_detected` is used somewhere else -/// than `MIPS` targets. -#[cfg(not(target_arch = "mips"))] -#[macro_export] -#[unstable(feature = "stdsimd", issue = "27731")] -macro_rules! is_mips_feature_detected { - ($t:tt) => { - compile_error!( - r#" - is_mips_feature_detected can only be used on MIPS targets. - You can prevent it from being used in other architectures by - guarding it behind a cfg(target_arch) as follows: - - #[cfg(target_arch = "mips")] { - if is_mips_feature_detected(...) { ... } - } - "# - ) - }; -} - -/// Prevents compilation if `is_mips64_feature_detected` is used somewhere else -/// than `MIPS64` targets. -#[cfg(not(target_arch = "mips64"))] -#[macro_export] -#[unstable(feature = "stdsimd", issue = "27731")] -macro_rules! is_mips64_feature_detected { - ($t:tt) => { - compile_error!( - r#" - is_mips64_feature_detected can only be used on MIPS64 targets. - You can prevent it from being used in other architectures by - guarding it behind a cfg(target_arch) as follows: - - #[cfg(target_arch = "mips64")] { - if is_mips64_feature_detected(...) { ... } - } - "# - ) - }; -} diff --git a/library/stdarch/crates/std_detect/src/detect/macros.rs b/library/stdarch/crates/std_detect/src/detect/macros.rs index b83fe71c41..fa7189336a 100644 --- a/library/stdarch/crates/std_detect/src/detect/macros.rs +++ b/library/stdarch/crates/std_detect/src/detect/macros.rs @@ -2,6 +2,7 @@ macro_rules! features { ( @TARGET: $target:ident; + @CFG: $cfg:meta; @MACRO_NAME: $macro_name:ident; @MACRO_ATTRS: $(#[$macro_attrs:meta])* $(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; )* @@ -11,6 +12,8 @@ macro_rules! features { #[macro_export] $(#[$macro_attrs])* #[allow_internal_unstable(stdsimd_internal)] + #[cfg($cfg)] + #[doc(cfg($cfg))] macro_rules! $macro_name { $( ($feature_lit) => { @@ -44,6 +47,50 @@ macro_rules! features { }; } + $(#[$macro_attrs])* + #[macro_export] + #[cfg(not($cfg))] + #[doc(cfg($cfg))] + macro_rules! $macro_name { + $( + ($feature_lit) => { + compile_error!( + concat!( + r#"This macro cannot be used on the current target. + You can prevent it from being used in other architectures by + guarding it behind a cfg("#, + stringify!($cfg), + ")." + ) + ) + }; + )* + $( + ($bind_feature) => { $macro_name!($feature_impl) }; + )* + $( + ($nort_feature) => { + compile_error!( + concat!( + stringify!($nort_feature), + " feature cannot be detected at run-time" + ) + ) + }; + )* + ($t:tt,) => { + $macro_name!($t); + }; + ($t:tt) => { + compile_error!( + concat!( + concat!("unknown ", stringify!($target)), + concat!(" target feature: ", $t) + ) + ) + }; + } + /// Each variant denotes a position in a bitset for a particular feature. /// /// PLEASE: do not use this, it is an implementation detail subject @@ -53,6 +100,7 @@ macro_rules! features { #[derive(Copy, Clone)] #[repr(u8)] #[unstable(feature = "stdsimd_internal", issue = "none")] + #[cfg($cfg)] pub(crate) enum Feature { $( $(#[$feature_comment])* @@ -63,6 +111,7 @@ macro_rules! features { _last } + #[cfg($cfg)] impl Feature { pub(crate) fn to_str(self) -> &'static str { match self { @@ -86,6 +135,7 @@ macro_rules! features { /// PLEASE: do not use this, it is an implementation detail subject /// to change. #[doc(hidden)] + #[cfg($cfg)] pub mod __is_feature_detected { $( diff --git a/library/stdarch/crates/std_detect/src/detect/mod.rs b/library/stdarch/crates/std_detect/src/detect/mod.rs index e4eea72d45..2bca84ca12 100644 --- a/library/stdarch/crates/std_detect/src/detect/mod.rs +++ b/library/stdarch/crates/std_detect/src/detect/mod.rs @@ -19,65 +19,10 @@ use cfg_if::cfg_if; -#[macro_use] -mod error_macros; - #[macro_use] mod macros; -cfg_if! { - if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - #[path = "arch/x86.rs"] - #[macro_use] - mod arch; - } else if #[cfg(target_arch = "arm")] { - #[path = "arch/arm.rs"] - #[macro_use] - mod arch; - } else if #[cfg(target_arch = "aarch64")] { - #[path = "arch/aarch64.rs"] - #[macro_use] - mod arch; - } else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { - #[path = "arch/riscv.rs"] - #[macro_use] - mod arch; - } else if #[cfg(target_arch = "powerpc")] { - #[path = "arch/powerpc.rs"] - #[macro_use] - mod arch; - } else if #[cfg(target_arch = "powerpc64")] { - #[path = "arch/powerpc64.rs"] - #[macro_use] - mod arch; - } else if #[cfg(target_arch = "mips")] { - #[path = "arch/mips.rs"] - #[macro_use] - mod arch; - } else if #[cfg(target_arch = "mips64")] { - #[path = "arch/mips64.rs"] - #[macro_use] - mod arch; - } else { - // Unimplemented architecture: - #[allow(dead_code)] - mod arch { - #[doc(hidden)] - pub(crate) enum Feature { - Null - } - #[doc(hidden)] - pub mod __is_feature_detected {} - - impl Feature { - #[doc(hidden)] - pub(crate) fn from_str(_s: &str) -> Result { Err(()) } - #[doc(hidden)] - pub(crate) fn to_str(self) -> &'static str { "" } - } - } - } -} +mod arch; // This module needs to be public because the `is_{arch}_feature_detected!` // macros expand calls to items within it in user crates. diff --git a/library/stdarch/crates/std_detect/src/detect/os/aarch64.rs b/library/stdarch/crates/std_detect/src/detect/os/aarch64.rs index 0fd2702731..169c51b961 100644 --- a/library/stdarch/crates/std_detect/src/detect/os/aarch64.rs +++ b/library/stdarch/crates/std_detect/src/detect/os/aarch64.rs @@ -87,7 +87,11 @@ pub(crate) fn detect_features() -> cache::Initializer { ); } + // Check for either APA or API field + enable_feature(Feature::paca, bits_shift(aa64isar1, 11, 4) >= 1); enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1); + // Check for either GPA or GPI field + enable_feature(Feature::pacg, bits_shift(aa64isar1, 31, 24) >= 1); } value diff --git a/library/stdarch/crates/std_detect/src/detect/os/linux/aarch64.rs b/library/stdarch/crates/std_detect/src/detect/os/linux/aarch64.rs index 4dc6362057..b6a2e5218c 100644 --- a/library/stdarch/crates/std_detect/src/detect/os/linux/aarch64.rs +++ b/library/stdarch/crates/std_detect/src/detect/os/linux/aarch64.rs @@ -230,8 +230,8 @@ impl AtHwcap { enable_feature(Feature::flagm, self.flagm); enable_feature(Feature::ssbs, self.ssbs); enable_feature(Feature::sb, self.sb); - // FEAT_PAuth provides both paca & pacg - enable_feature(Feature::pauth, self.paca && self.pacg); + enable_feature(Feature::paca, self.paca); + enable_feature(Feature::pacg, self.pacg); enable_feature(Feature::dpb, self.dcpop); enable_feature(Feature::dpb2, self.dcpodp); enable_feature(Feature::rand, self.rng); diff --git a/library/stdarch/crates/std_detect/src/lib.rs b/library/stdarch/crates/std_detect/src/lib.rs index a17bd40e04..c0e0de0dd5 100644 --- a/library/stdarch/crates/std_detect/src/lib.rs +++ b/library/stdarch/crates/std_detect/src/lib.rs @@ -18,18 +18,17 @@ #![allow(clippy::shadow_reuse)] #![deny(clippy::missing_inline_in_public_items)] #![cfg_attr(test, allow(unused_imports))] -#![cfg_attr(feature = "std_detect_file_io", feature(vec_spare_capacity))] #![no_std] -// rust-lang/rust#83888: removing `extern crate` gives an error that `vec_spare_capacity` is unknown -#[cfg_attr(feature = "std_detect_file_io", allow(unused_extern_crates))] -#[cfg(feature = "std_detect_file_io")] -extern crate alloc; - #[cfg(test)] #[macro_use] extern crate std; +// rust-lang/rust#83888: removing `extern crate` gives an error that `vec_spare> +#[cfg_attr(feature = "std_detect_file_io", allow(unused_extern_crates))] +#[cfg(feature = "std_detect_file_io")] +extern crate alloc; + #[doc(hidden)] #[unstable(feature = "stdsimd", issue = "27731")] pub mod detect; diff --git a/library/stdarch/crates/std_detect/tests/cpu-detection.rs b/library/stdarch/crates/std_detect/tests/cpu-detection.rs index adbe4fa9a7..ca8bf28f44 100644 --- a/library/stdarch/crates/std_detect/tests/cpu-detection.rs +++ b/library/stdarch/crates/std_detect/tests/cpu-detection.rs @@ -55,7 +55,8 @@ fn aarch64_linux() { println!("flagm: {}", is_aarch64_feature_detected!("flagm")); println!("ssbs: {}", is_aarch64_feature_detected!("ssbs")); println!("sb: {}", is_aarch64_feature_detected!("sb")); - println!("pauth: {}", is_aarch64_feature_detected!("pauth")); + println!("paca: {}", is_aarch64_feature_detected!("paca")); + println!("pacg: {}", is_aarch64_feature_detected!("pacg")); println!("dpb: {}", is_aarch64_feature_detected!("dpb")); println!("dpb2: {}", is_aarch64_feature_detected!("dpb2")); println!("sve2: {}", is_aarch64_feature_detected!("sve2")); diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 04dab6b804..2da41484ca 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "test" version = "0.0.0" -edition = "2018" +edition = "2021" [lib] crate-type = ["dylib", "rlib"] diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index cb40b4e965..b39701a3d4 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -109,12 +109,10 @@ fn optgroups() -> getopts::Options { unstable-options = Allow use of experimental features", "unstable-options", ) - .optflagopt( + .optflag( "", "report-time", - "Show execution time of each test. Available values: - plain = do not colorize the execution time (default); - colored = colorize output according to the `color` parameter value; + "Show execution time of each test. Threshold values for colorized output can be configured via `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and @@ -125,7 +123,6 @@ fn optgroups() -> getopts::Options { is 0.5 seconds, and the critical time is 2 seconds. Not available for --format=terse", - "plain|colored", ) .optflag( "", @@ -319,17 +316,12 @@ fn get_time_options( allow_unstable: bool, ) -> OptPartRes> { let report_time = unstable_optflag!(matches, allow_unstable, "report-time"); - let colored_opt_str = matches.opt_str("report-time"); - let mut report_time_colored = report_time && colored_opt_str == Some("colored".into()); let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time"); // If `ensure-test-time` option is provided, time output is enforced, // so user won't be confused if any of tests will silently fail. let options = if report_time || ensure_test_time { - if ensure_test_time && !report_time { - report_time_colored = true; - } - Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored)) + Some(TestTimeOptions::new_from_env(ensure_test_time)) } else { None }; diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 9c261e8cc8..920f55ad25 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -47,7 +47,6 @@ pub struct ConsoleTestState { pub passed: usize, pub failed: usize, pub ignored: usize, - pub allowed_fail: usize, pub filtered_out: usize, pub measured: usize, pub exec_time: Option, @@ -71,7 +70,6 @@ impl ConsoleTestState { passed: 0, failed: 0, ignored: 0, - allowed_fail: 0, filtered_out: 0, measured: 0, exec_time: None, @@ -112,7 +110,6 @@ impl ConsoleTestState { TestResult::TrFailed => "failed".to_owned(), TestResult::TrFailedMsg(ref msg) => format!("failed: {}", msg), TestResult::TrIgnored => "ignored".to_owned(), - TestResult::TrAllowedFail => "failed (allowed)".to_owned(), TestResult::TrBench(ref bs) => fmt_bench_samples(bs), TestResult::TrTimedFail => "failed (time limit exceeded)".to_owned(), }, @@ -126,7 +123,7 @@ impl ConsoleTestState { } fn current_test_count(&self) -> usize { - self.passed + self.failed + self.ignored + self.measured + self.allowed_fail + self.passed + self.failed + self.ignored + self.measured } } @@ -191,7 +188,6 @@ fn handle_test_result(st: &mut ConsoleTestState, completed_test: CompletedTest) st.not_failures.push((test, stdout)); } TestResult::TrIgnored => st.ignored += 1, - TestResult::TrAllowedFail => st.allowed_fail += 1, TestResult::TrBench(bs) => { st.metrics.insert_metric( test.name.as_slice(), diff --git a/library/test/src/formatters/json.rs b/library/test/src/formatters/json.rs index 424d3ef7b4..c089bfc479 100644 --- a/library/test/src/formatters/json.rs +++ b/library/test/src/formatters/json.rs @@ -124,15 +124,6 @@ impl OutputFormatter for JsonFormatter { self.write_event("test", desc.name.as_slice(), "ignored", exec_time, stdout, None) } - TestResult::TrAllowedFail => self.write_event( - "test", - desc.name.as_slice(), - "allowed_failure", - exec_time, - stdout, - None, - ), - TestResult::TrBench(ref bs) => { let median = bs.ns_iter_summ.median as usize; let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize; @@ -172,14 +163,12 @@ impl OutputFormatter for JsonFormatter { \"event\": \"{}\", \ \"passed\": {}, \ \"failed\": {}, \ - \"allowed_fail\": {}, \ \"ignored\": {}, \ \"measured\": {}, \ \"filtered_out\": {}", if state.failed == 0 { "ok" } else { "failed" }, state.passed, - state.failed + state.allowed_fail, - state.allowed_fail, + state.failed, state.ignored, state.measured, state.filtered_out, diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index fa23cf2689..54e9860ab5 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -33,7 +33,6 @@ impl OutputFormatter for JunitFormatter { _shuffle_seed: Option, ) -> io::Result<()> { // We write xml header on run start - self.out.write_all(b"\n")?; self.write_message("") } @@ -122,7 +121,7 @@ impl OutputFormatter for JunitFormatter { ))?; } - TestResult::TrOk | TestResult::TrAllowedFail => { + TestResult::TrOk => { self.write_message(&*format!( "", @@ -138,7 +137,7 @@ impl OutputFormatter for JunitFormatter { self.write_message("")?; self.write_message("")?; - self.out.write_all(b"\n\n")?; + self.out.write_all(b"\n")?; Ok(state.failed == 0) } diff --git a/library/test/src/formatters/pretty.rs b/library/test/src/formatters/pretty.rs index 4a03b4b914..041df5216d 100644 --- a/library/test/src/formatters/pretty.rs +++ b/library/test/src/formatters/pretty.rs @@ -49,10 +49,6 @@ impl PrettyFormatter { self.write_short_result("ignored", term::color::YELLOW) } - pub fn write_allowed_fail(&mut self) -> io::Result<()> { - self.write_short_result("FAILED (allowed)", term::color::YELLOW) - } - pub fn write_time_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED (time limit exceeded)", term::color::RED) } @@ -102,7 +98,7 @@ impl PrettyFormatter { if let (Some(opts), Some(time)) = (self.time_options, exec_time) { let time_str = format!(" <{}>", time); - let color = if opts.colored { + let color = if self.use_color { if opts.is_critical(desc, time) { Some(term::color::RED) } else if opts.is_warn(desc, time) { @@ -219,7 +215,6 @@ impl OutputFormatter for PrettyFormatter { TestResult::TrOk => self.write_ok()?, TestResult::TrFailed | TestResult::TrFailedMsg(_) => self.write_failed()?, TestResult::TrIgnored => self.write_ignored()?, - TestResult::TrAllowedFail => self.write_allowed_fail()?, TestResult::TrBench(ref bs) => { self.write_bench()?; self.write_plain(&format!(": {}", fmt_bench_samples(bs)))?; @@ -263,22 +258,10 @@ impl OutputFormatter for PrettyFormatter { self.write_pretty("FAILED", term::color::RED)?; } - let s = if state.allowed_fail > 0 { - format!( - ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out", - state.passed, - state.failed + state.allowed_fail, - state.allowed_fail, - state.ignored, - state.measured, - state.filtered_out - ) - } else { - format!( - ". {} passed; {} failed; {} ignored; {} measured; {} filtered out", - state.passed, state.failed, state.ignored, state.measured, state.filtered_out - ) - }; + let s = format!( + ". {} passed; {} failed; {} ignored; {} measured; {} filtered out", + state.passed, state.failed, state.ignored, state.measured, state.filtered_out + ); self.write_plain(&s)?; diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 1f2c410cd9..12aca7cd9a 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -54,10 +54,6 @@ impl TerseFormatter { self.write_short_result("i", term::color::YELLOW) } - pub fn write_allowed_fail(&mut self) -> io::Result<()> { - self.write_short_result("a", term::color::YELLOW) - } - pub fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } @@ -207,7 +203,6 @@ impl OutputFormatter for TerseFormatter { self.write_failed() } TestResult::TrIgnored => self.write_ignored(), - TestResult::TrAllowedFail => self.write_allowed_fail(), TestResult::TrBench(ref bs) => { if self.is_multithreaded { self.write_test_name(desc)?; @@ -244,22 +239,10 @@ impl OutputFormatter for TerseFormatter { self.write_pretty("FAILED", term::color::RED)?; } - let s = if state.allowed_fail > 0 { - format!( - ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out", - state.passed, - state.failed + state.allowed_fail, - state.allowed_fail, - state.ignored, - state.measured, - state.filtered_out - ) - } else { - format!( - ". {} passed; {} failed; {} ignored; {} measured; {} filtered out", - state.passed, state.failed, state.ignored, state.measured, state.filtered_out - ) - }; + let s = format!( + ". {} passed; {} failed; {} ignored; {} measured; {} filtered out", + state.passed, state.failed, state.ignored, state.measured, state.filtered_out + ); self.write_plain(&s)?; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index fad83094cd..8fc2b4ed74 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -20,6 +20,7 @@ #![feature(internal_output_capture)] #![feature(staged_api)] #![feature(termination_trait_lib)] +#![feature(process_exitcode_placeholder)] #![feature(test)] #![feature(total_cmp)] @@ -182,7 +183,7 @@ fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn { /// Tests is considered a failure. By default, invokes `report()` /// and checks for a `0` result. pub fn assert_test_result(result: T) { - let code = result.report(); + let code = result.report().to_i32(); assert_eq!( code, 0, "the test returned a termination value with a non-zero status code ({}) \ diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index c5c56ca9c7..8c216a1e0e 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -19,7 +19,6 @@ pub enum TestResult { TrFailed, TrFailedMsg(String), TrIgnored, - TrAllowedFail, TrBench(BenchSamples), TrTimedFail, } @@ -42,8 +41,6 @@ pub fn calc_result<'a>( if maybe_panic_str.map(|e| e.contains(msg)).unwrap_or(false) { TestResult::TrOk - } else if desc.allow_fail { - TestResult::TrAllowedFail } else if let Some(panic_str) = maybe_panic_str { TestResult::TrFailedMsg(format!( r#"panic did not contain expected string @@ -64,7 +61,6 @@ pub fn calc_result<'a>( (&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => { TestResult::TrFailedMsg("test did not panic as expected".to_string()) } - _ if desc.allow_fail => TestResult::TrAllowedFail, _ => TestResult::TrFailed, }; @@ -90,11 +86,10 @@ pub fn get_result_from_exit_code( time_opts: &Option, exec_time: &Option, ) -> TestResult { - let result = match (desc.allow_fail, code) { - (_, TR_OK) => TestResult::TrOk, - (true, TR_FAILED) => TestResult::TrAllowedFail, - (false, TR_FAILED) => TestResult::TrFailed, - (_, _) => TestResult::TrFailedMsg(format!("got unexpected return code {}", code)), + let result = match code { + TR_OK => TestResult::TrOk, + TR_FAILED => TestResult::TrFailed, + _ => TestResult::TrFailedMsg(format!("got unexpected return code {}", code)), }; // If test is already failed (or allowed to fail), do not change the result. diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index 718613895d..d566dbc09f 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -62,10 +62,11 @@ fn one_ignored_one_unignored_test() -> Vec { name: StaticTestName("1"), ignore: true, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(move || {})), }, @@ -74,10 +75,11 @@ fn one_ignored_one_unignored_test() -> Vec { name: StaticTestName("2"), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(move || {})), }, @@ -94,10 +96,11 @@ pub fn do_not_run_ignored_tests() { name: StaticTestName("whatever"), ignore: true, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -115,10 +118,11 @@ pub fn ignored_tests_result_in_ignored() { name: StaticTestName("whatever"), ignore: true, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -140,10 +144,11 @@ fn test_should_panic() { name: StaticTestName("whatever"), ignore: false, should_panic: ShouldPanic::Yes, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -165,10 +170,11 @@ fn test_should_panic_good_message() { name: StaticTestName("whatever"), ignore: false, should_panic: ShouldPanic::YesWithMessage("error message"), - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -195,10 +201,11 @@ fn test_should_panic_bad_message() { name: StaticTestName("whatever"), ignore: false, should_panic: ShouldPanic::YesWithMessage(expected), - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -229,10 +236,11 @@ fn test_should_panic_non_string_message_type() { name: StaticTestName("whatever"), ignore: false, should_panic: ShouldPanic::YesWithMessage(expected), - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -255,10 +263,11 @@ fn test_should_panic_but_succeeds() { name: StaticTestName("whatever"), ignore: false, should_panic, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -289,10 +298,11 @@ fn report_time_test_template(report_time: bool) -> Option { name: StaticTestName("whatever"), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -324,10 +334,11 @@ fn time_test_failure_template(test_type: TestType) -> TestResult { name: StaticTestName("whatever"), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(f)), }; @@ -363,10 +374,11 @@ fn typed_test_desc(test_type: TestType) -> TestDesc { name: StaticTestName("whatever"), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type, + #[cfg(bootstrap)] + allow_fail: false, } } @@ -382,7 +394,6 @@ fn test_time_options_threshold() { let options = TestTimeOptions { error_on_excess: false, - colored: false, unit_threshold: unit.clone(), integration_threshold: integration.clone(), doctest_threshold: doc.clone(), @@ -476,10 +487,11 @@ pub fn exclude_should_panic_option() { name: StaticTestName("3"), ignore: false, should_panic: ShouldPanic::Yes, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(move || {})), }); @@ -493,17 +505,18 @@ pub fn exclude_should_panic_option() { #[test] pub fn exact_filter_match() { fn tests() -> Vec { - vec!["base", "base::test", "base::test1", "base::test2"] + ["base", "base::test", "base::test1", "base::test2"] .into_iter() .map(|name| TestDescAndFn { desc: TestDesc { name: StaticTestName(name), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(move || {})), }) @@ -589,10 +602,11 @@ fn sample_tests() -> Vec { name: DynTestName((*name).clone()), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }, testfn: DynTestFn(Box::new(testfn)), }; @@ -740,10 +754,11 @@ pub fn test_bench_no_iter() { name: StaticTestName("f"), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }; crate::bench::benchmark(TestId(0), desc, tx, true, f); @@ -762,10 +777,11 @@ pub fn test_bench_iter() { name: StaticTestName("f"), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }; crate::bench::benchmark(TestId(0), desc, tx, true, f); @@ -778,20 +794,22 @@ fn should_sort_failures_before_printing_them() { name: StaticTestName("a"), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }; let test_b = TestDesc { name: StaticTestName("b"), ignore: false, should_panic: ShouldPanic::No, - allow_fail: false, compile_fail: false, no_run: false, test_type: TestType::Unknown, + #[cfg(bootstrap)] + allow_fail: false, }; let mut out = PrettyFormatter::new(OutputLocation::Raw(Vec::new()), false, 10, false, None); @@ -802,7 +820,6 @@ fn should_sort_failures_before_printing_them() { passed: 0, failed: 0, ignored: 0, - allowed_fail: 0, filtered_out: 0, measured: 0, exec_time: None, diff --git a/library/test/src/time.rs b/library/test/src/time.rs index e0b6eadffa..8c64e5d1b7 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -137,14 +137,13 @@ pub struct TestTimeOptions { /// Denotes if the test critical execution time limit excess should be considered /// a test failure. pub error_on_excess: bool, - pub colored: bool, pub unit_threshold: TimeThreshold, pub integration_threshold: TimeThreshold, pub doctest_threshold: TimeThreshold, } impl TestTimeOptions { - pub fn new_from_env(error_on_excess: bool, colored: bool) -> Self { + pub fn new_from_env(error_on_excess: bool) -> Self { let unit_threshold = TimeThreshold::from_env_var(time_constants::UNIT_ENV_NAME) .unwrap_or_else(Self::default_unit); @@ -155,7 +154,7 @@ impl TestTimeOptions { let doctest_threshold = TimeThreshold::from_env_var(time_constants::DOCTEST_ENV_NAME) .unwrap_or_else(Self::default_doctest); - Self { error_on_excess, colored, unit_threshold, integration_threshold, doctest_threshold } + Self { error_on_excess, unit_threshold, integration_threshold, doctest_threshold } } pub fn is_warn(&self, test: &TestDesc, exec_time: &TestExecTime) -> bool { diff --git a/library/test/src/types.rs b/library/test/src/types.rs index 37bb38fb0d..43e5a10ebb 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -118,10 +118,11 @@ pub struct TestDesc { pub name: TestName, pub ignore: bool, pub should_panic: options::ShouldPanic, - pub allow_fail: bool, pub compile_fail: bool, pub no_run: bool, pub test_type: TestType, + #[cfg(bootstrap)] + pub allow_fail: bool, } impl TestDesc { @@ -150,9 +151,6 @@ impl TestDesc { } options::ShouldPanic::No => {} } - if self.allow_fail { - return Some("allow fail"); - } if self.compile_fail { return Some("compile fail"); } diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 1941f2b5a0..69fce8d779 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -3,7 +3,7 @@ name = "unwind" version = "0.0.0" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" -edition = "2018" +edition = "2021" include = [ '/libunwind/*', ] diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 5e15fe75a2..c8c5528b10 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -42,6 +42,9 @@ 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 = "m68k")] +pub const unwinder_private_data_size: usize = 2; + #[cfg(target_arch = "mips")] pub const unwinder_private_data_size: usize = 2; diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index b68b2163f8..592a137e37 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -44,14 +44,22 @@ libc = "0.2" serde = { version = "1.0.8", features = ["derive"] } serde_json = "1.0.2" toml = "0.5" -time = "0.1" ignore = "0.4.10" opener = "0.5" once_cell = "1.7.2" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" -features = ["fileapi", "ioapiset", "jobapi2", "handleapi", "winioctl", "psapi", "impl-default"] +features = [ + "fileapi", + "ioapiset", + "jobapi2", + "handleapi", + "winioctl", + "psapi", + "impl-default", + "timezoneapi", +] [dev-dependencies] -pretty_assertions = "0.6" +pretty_assertions = "0.7" diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index e730a2557e..9c41ab69c8 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -30,6 +30,7 @@ fn main() { println!("{}", suggestion); } + let pre_commit = config.src.join(".git").join("hooks").join("pre-commit"); Build::new(config).build(); if suggest_setup { @@ -42,6 +43,19 @@ fn main() { println!("{}", suggestion); } + // Give a warning if the pre-commit script is in pre-commit and not pre-push. + // HACK: Since the commit script uses hard links, we can't actually tell if it was installed by x.py setup or not. + // We could see if it's identical to src/etc/pre-push.sh, but pre-push may have been modified in the meantime. + // Instead, look for this comment, which is almost certainly not in any custom hook. + if std::fs::read_to_string(pre_commit).map_or(false, |contents| { + contents.contains("https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570") + }) { + println!( + "warning: You have the pre-push script installed to .git/hooks/pre-commit. \ + Consider moving it to .git/hooks/pre-push instead, which runs less often." + ); + } + if suggest_setup || changelog_suggestion.is_some() { println!("note: this message was printed twice to make it more likely to be seen"); } diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 7c36bb264c..6c1128b393 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -63,7 +63,7 @@ def support_xz(): except tarfile.CompressionError: return False -def get(base, url, path, checksums, verbose=False, do_verify=True): +def get(base, url, path, checksums, verbose=False, do_verify=True, help_on_error=None): with tempfile.NamedTemporaryFile(delete=False) as temp_file: temp_path = temp_file.name @@ -82,7 +82,7 @@ def get(base, url, path, checksums, verbose=False, do_verify=True): print("ignoring already-download file", path, "due to failed verification") os.unlink(path) - download(temp_path, "{}/{}".format(base, url), True, verbose) + download(temp_path, "{}/{}".format(base, url), True, verbose, help_on_error=help_on_error) if do_verify and not verify(temp_path, sha256, verbose): raise RuntimeError("failed verification") if verbose: @@ -95,17 +95,17 @@ def get(base, url, path, checksums, verbose=False, do_verify=True): os.unlink(temp_path) -def download(path, url, probably_big, verbose): +def download(path, url, probably_big, verbose, help_on_error=None): for _ in range(0, 4): try: - _download(path, url, probably_big, verbose, True) + _download(path, url, probably_big, verbose, True, help_on_error=help_on_error) return except RuntimeError: print("\nspurious failure, trying again") - _download(path, url, probably_big, verbose, False) + _download(path, url, probably_big, verbose, False, help_on_error=help_on_error) -def _download(path, url, probably_big, verbose, exception): +def _download(path, url, probably_big, verbose, exception, help_on_error=None): if probably_big or verbose: print("downloading {}".format(url)) # see https://serverfault.com/questions/301128/how-to-download @@ -126,7 +126,8 @@ def _download(path, url, probably_big, verbose, exception): "--connect-timeout", "30", # timeout if cannot connect within 30 seconds "--retry", "3", "-Sf", "-o", path, url], verbose=verbose, - exception=exception) + exception=exception, + help_on_error=help_on_error) def verify(path, expected, verbose): @@ -167,7 +168,7 @@ def unpack(tarball, tarball_suffix, dst, verbose=False, match=None): shutil.rmtree(os.path.join(dst, fname)) -def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs): +def run(args, verbose=False, exception=False, is_bootstrap=False, help_on_error=None, **kwargs): """Run a child program in a new process""" if verbose: print("running: " + ' '.join(args)) @@ -178,6 +179,8 @@ def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs): code = ret.wait() if code != 0: err = "failed to run: " + ' '.join(args) + if help_on_error is not None: + err += "\n" + help_on_error if verbose or exception: raise RuntimeError(err) # For most failures, we definitely do want to print this error, or the user will have no @@ -624,6 +627,14 @@ class RustBuild(object): filename = "rust-dev-nightly-" + self.build + tarball_suffix tarball = os.path.join(rustc_cache, filename) if not os.path.exists(tarball): + help_on_error = "error: failed to download llvm from ci" + help_on_error += "\nhelp: old builds get deleted after a certain time" + help_on_error += "\nhelp: if trying to compile an old commit of rustc," + help_on_error += " disable `download-ci-llvm` in config.toml:" + help_on_error += "\n" + help_on_error += "\n[llvm]" + help_on_error += "\ndownload-ci-llvm = false" + help_on_error += "\n" get( base, "{}/{}".format(url, filename), @@ -631,6 +642,7 @@ class RustBuild(object): self.checksums_sha256, verbose=self.verbose, do_verify=False, + help_on_error=help_on_error, ) unpack(tarball, tarball_suffix, self.llvm_root(), match="rust-dev", @@ -863,7 +875,7 @@ class RustBuild(object): >>> rb.get_toml("key2") 'value2' - If the key does not exists, the result is None: + If the key does not exist, the result is None: >>> rb.get_toml("key3") is None True @@ -1221,9 +1233,9 @@ def bootstrap(help_triggered): build.verbose = args.verbose build.clean = args.clean - # Read from `RUST_BOOTSTRAP_CONFIG`, then `--config`, then fallback to `config.toml` (if it + # Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then fallback to `config.toml` (if it # exists). - toml_path = os.getenv('RUST_BOOTSTRAP_CONFIG') or args.config + toml_path = args.config or os.getenv('RUST_BOOTSTRAP_CONFIG') if not toml_path and os.path.exists('config.toml'): toml_path = 'config.toml' diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py index 7bffc1c152..06ca3ce21b 100644 --- a/src/bootstrap/bootstrap_test.py +++ b/src/bootstrap/bootstrap_test.py @@ -55,8 +55,8 @@ class ProgramOutOfDate(unittest.TestCase): def tearDown(self): rmtree(self.container) - def test_stamp_path_does_not_exists(self): - """Return True when the stamp file does not exists""" + def test_stamp_path_does_not_exist(self): + """Return True when the stamp file does not exist""" 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.key)) diff --git a/src/bootstrap/build.rs b/src/bootstrap/build.rs index d40b924e0f..6e39ea00f8 100644 --- a/src/bootstrap/build.rs +++ b/src/bootstrap/build.rs @@ -3,6 +3,8 @@ use std::path::PathBuf; fn main() { println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=RUSTC"); + println!("cargo:rerun-if-env-changed=PATH"); println!("cargo:rustc-env=BUILD_TRIPLE={}", env::var("HOST").unwrap()); // This may not be a canonicalized path. diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 6ccf8b1d52..0d387ff1e3 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; use std::fs; use std::hash::Hash; use std::ops::Deref; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; use std::process::Command; use std::time::{Duration, Instant}; @@ -26,7 +26,7 @@ use crate::run; use crate::test; use crate::tool::{self, SourceType}; use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir}; -use crate::{Build, DocTests, GitRepo, Mode}; +use crate::{Build, CLang, DocTests, GitRepo, Mode}; pub use crate::Compiler; // FIXME: replace with std::lazy after it gets stabilized and reaches beta @@ -105,6 +105,44 @@ struct StepDescription { should_run: fn(ShouldRun<'_>) -> ShouldRun<'_>, make_run: fn(RunConfig<'_>), name: &'static str, + kind: Kind, +} + +#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)] +pub struct TaskPath { + pub path: PathBuf, + pub kind: Option, +} + +impl TaskPath { + pub fn parse(path: impl Into) -> TaskPath { + let mut kind = None; + let mut path = path.into(); + + let mut components = path.components(); + if let Some(Component::Normal(os_str)) = components.next() { + if let Some(str) = os_str.to_str() { + if let Some((found_kind, found_prefix)) = str.split_once("::") { + if found_kind.is_empty() { + panic!("empty kind in task path {}", path.display()); + } + kind = Some(Kind::parse(found_kind)); + path = Path::new(found_prefix).join(components.as_path()); + } + } + } + + TaskPath { path, kind } + } +} + +impl Debug for TaskPath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(kind) = &self.kind { + write!(f, "{}::", kind.as_str())?; + } + write!(f, "{}", self.path.display()) + } } /// Collection of paths used to match a task rule. @@ -115,14 +153,14 @@ pub enum PathSet { /// These are generally matched as a path suffix. For example, a /// command-line value of `libstd` will match if `src/libstd` is in the /// set. - Set(BTreeSet), + Set(BTreeSet), /// A "suite" of paths. /// /// These can match as a path suffix (like `Set`), or as a prefix. For /// example, a command-line value of `src/test/ui/abi/variadic-ffi.rs` /// will match `src/test/ui`. A command-line value of `ui` would also /// match `src/test/ui`. - Suite(PathBuf), + Suite(TaskPath), } impl PathSet { @@ -130,35 +168,46 @@ impl PathSet { PathSet::Set(BTreeSet::new()) } - fn one>(path: P) -> PathSet { + fn one>(path: P, kind: Kind) -> PathSet { let mut set = BTreeSet::new(); - set.insert(path.into()); + set.insert(TaskPath { path: path.into(), kind: Some(kind.into()) }); PathSet::Set(set) } - fn has(&self, needle: &Path) -> bool { + fn has(&self, needle: &Path, module: Option) -> bool { + let check = |p: &TaskPath| { + if let (Some(p_kind), Some(kind)) = (&p.kind, module) { + p.path.ends_with(needle) && *p_kind == kind + } else { + p.path.ends_with(needle) + } + }; + match self { - PathSet::Set(set) => set.iter().any(|p| p.ends_with(needle)), - PathSet::Suite(suite) => suite.ends_with(needle), + PathSet::Set(set) => set.iter().any(check), + PathSet::Suite(suite) => check(suite), } } fn path(&self, builder: &Builder<'_>) -> PathBuf { match self { - PathSet::Set(set) => set.iter().next().unwrap_or(&builder.build.src).to_path_buf(), - PathSet::Suite(path) => PathBuf::from(path), + PathSet::Set(set) => { + set.iter().next().map(|p| &p.path).unwrap_or(&builder.build.src).clone() + } + PathSet::Suite(path) => path.path.clone(), } } } impl StepDescription { - fn from() -> StepDescription { + fn from(kind: Kind) -> StepDescription { StepDescription { default: S::DEFAULT, only_hosts: S::ONLY_HOSTS, should_run: S::should_run, make_run: S::make_run, name: std::any::type_name::(), + kind, } } @@ -177,7 +226,7 @@ impl StepDescription { } fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool { - if builder.config.exclude.iter().any(|e| pathset.has(e)) { + if builder.config.exclude.iter().any(|e| pathset.has(&e.path, e.kind)) { eprintln!("Skipping {:?} because it is excluded", pathset); return true; } @@ -192,8 +241,10 @@ impl StepDescription { } fn run(v: &[StepDescription], builder: &Builder<'_>, paths: &[PathBuf]) { - let should_runs = - v.iter().map(|desc| (desc.should_run)(ShouldRun::new(builder))).collect::>(); + let should_runs = v + .iter() + .map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind))) + .collect::>(); // sanity checks on rules for (desc, should_run) in v.iter().zip(&should_runs) { @@ -226,7 +277,7 @@ impl StepDescription { if let Some(suite) = should_run.is_suite_path(path) { attempted_run = true; desc.maybe_run(builder, suite); - } else if let Some(pathset) = should_run.pathset_for_path(path) { + } else if let Some(pathset) = should_run.pathset_for_path(path, desc.kind) { attempted_run = true; desc.maybe_run(builder, pathset); } @@ -246,6 +297,8 @@ enum ReallyDefault<'a> { pub struct ShouldRun<'a> { pub builder: &'a Builder<'a>, + kind: Kind, + // use a BTreeSet to maintain sort order paths: BTreeSet, @@ -255,9 +308,10 @@ pub struct ShouldRun<'a> { } impl<'a> ShouldRun<'a> { - fn new(builder: &'a Builder<'_>) -> ShouldRun<'a> { + fn new(builder: &'a Builder<'_>, kind: Kind) -> ShouldRun<'a> { ShouldRun { builder, + kind, paths: BTreeSet::new(), is_really_default: ReallyDefault::Bool(true), // by default no additional conditions } @@ -293,7 +347,7 @@ impl<'a> ShouldRun<'a> { let mut set = BTreeSet::new(); for krate in self.builder.in_tree_crates(name, None) { let path = krate.local_path(self.builder); - set.insert(path); + set.insert(TaskPath { path, kind: Some(self.kind) }); } self.paths.insert(PathSet::Set(set)); self @@ -306,7 +360,7 @@ impl<'a> ShouldRun<'a> { pub fn krate(mut self, name: &str) -> Self { for krate in self.builder.in_tree_crates(name, None) { let path = krate.local_path(self.builder); - self.paths.insert(PathSet::one(path)); + self.paths.insert(PathSet::one(path, self.kind)); } self } @@ -318,19 +372,25 @@ impl<'a> ShouldRun<'a> { // multiple aliases for the same job pub fn paths(mut self, paths: &[&str]) -> Self { - self.paths.insert(PathSet::Set(paths.iter().map(PathBuf::from).collect())); + self.paths.insert(PathSet::Set( + paths + .iter() + .map(|p| TaskPath { path: p.into(), kind: Some(self.kind.into()) }) + .collect(), + )); self } pub fn is_suite_path(&self, path: &Path) -> Option<&PathSet> { self.paths.iter().find(|pathset| match pathset { - PathSet::Suite(p) => path.starts_with(p), + PathSet::Suite(p) => path.starts_with(&p.path), PathSet::Set(_) => false, }) } pub fn suite_path(mut self, suite: &str) -> Self { - self.paths.insert(PathSet::Suite(PathBuf::from(suite))); + self.paths + .insert(PathSet::Suite(TaskPath { path: suite.into(), kind: Some(self.kind.into()) })); self } @@ -340,12 +400,12 @@ impl<'a> ShouldRun<'a> { self } - fn pathset_for_path(&self, path: &Path) -> Option<&PathSet> { - self.paths.iter().find(|pathset| pathset.has(path)) + fn pathset_for_path(&self, path: &Path, kind: Kind) -> Option<&PathSet> { + self.paths.iter().find(|pathset| pathset.has(path, Some(kind))) } } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub enum Kind { Build, Check, @@ -359,11 +419,44 @@ pub enum Kind { Run, } +impl Kind { + fn parse(string: &str) -> Kind { + match string { + "build" => Kind::Build, + "check" => Kind::Check, + "clippy" => Kind::Clippy, + "fix" => Kind::Fix, + "test" => Kind::Test, + "bench" => Kind::Bench, + "dist" => Kind::Dist, + "doc" => Kind::Doc, + "install" => Kind::Install, + "run" => Kind::Run, + other => panic!("unknown kind: {}", other), + } + } + + fn as_str(&self) -> &'static str { + match self { + Kind::Build => "build", + Kind::Check => "check", + Kind::Clippy => "clippy", + Kind::Fix => "fix", + Kind::Test => "test", + Kind::Bench => "bench", + Kind::Dist => "dist", + Kind::Doc => "doc", + Kind::Install => "install", + Kind::Run => "run", + } + } +} + impl<'a> Builder<'a> { fn get_step_descriptions(kind: Kind) -> Vec { macro_rules! describe { ($($rule:ty),+ $(,)?) => {{ - vec![$(StepDescription::from::<$rule>()),+] + vec![$(StepDescription::from::<$rule>(kind)),+] }}; } match kind { @@ -495,7 +588,6 @@ impl<'a> Builder<'a> { dist::RustcDev, dist::Analysis, dist::Src, - dist::PlainSourceTarball, dist::Cargo, dist::Rls, dist::RustAnalyzer, @@ -506,6 +598,11 @@ impl<'a> Builder<'a> { dist::LlvmTools, dist::RustDev, dist::Extended, + // It seems that PlainSourceTarball somehow changes how some of the tools + // perceive their dependencies (see #93033) which would invaliate fingerprints + // and force us to rebuild tools after vendoring dependencies. + // To work around this, create the Tarball after building all the tools. + dist::PlainSourceTarball, dist::BuildManifest, dist::ReproducibleArtifacts, ), @@ -540,8 +637,11 @@ impl<'a> Builder<'a> { let builder = Self::new_internal(build, kind, vec![]); let builder = &builder; - let mut should_run = ShouldRun::new(builder); + // The "build" kind here is just a placeholder, it will be replaced with something else in + // the following statement. + let mut should_run = ShouldRun::new(builder, Kind::Build); for desc in Builder::get_step_descriptions(builder.kind) { + should_run.kind = desc.kind; should_run = (desc.should_run)(should_run); } let mut help = String::from("Available paths:\n"); @@ -552,11 +652,11 @@ impl<'a> Builder<'a> { match pathset { PathSet::Set(set) => { for path in set { - add_path(&path); + add_path(&path.path); } } PathSet::Suite(path) => { - add_path(&path.join("...")); + add_path(&path.path.join("...")); } } } @@ -988,20 +1088,11 @@ impl<'a> Builder<'a> { } }; - // cfg(bootstrap) -- drop the compiler.stage == 0 branch. - if compiler.stage == 0 { - if use_new_symbol_mangling { - rustflags.arg("-Zsymbol-mangling-version=v0"); - } else { - rustflags.arg("-Zsymbol-mangling-version=legacy"); - } + if use_new_symbol_mangling { + rustflags.arg("-Csymbol-mangling-version=v0"); } else { - if use_new_symbol_mangling { - rustflags.arg("-Csymbol-mangling-version=v0"); - } else { - rustflags.arg("-Csymbol-mangling-version=legacy"); - rustflags.arg("-Zunstable-options"); - } + rustflags.arg("-Csymbol-mangling-version=legacy"); + rustflags.arg("-Zunstable-options"); } // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, @@ -1420,7 +1511,7 @@ impl<'a> Builder<'a> { let cc = ccacheify(&self.cc(target)); cargo.env(format!("CC_{}", target.triple), &cc); - let cflags = self.cflags(target, GitRepo::Rustc).join(" "); + let cflags = self.cflags(target, GitRepo::Rustc, CLang::C).join(" "); cargo.env(format!("CFLAGS_{}", target.triple), &cflags); if let Some(ar) = self.ar(target) { @@ -1432,9 +1523,10 @@ impl<'a> Builder<'a> { if let Ok(cxx) = self.cxx(target) { let cxx = ccacheify(&cxx); + let cxxflags = self.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); cargo .env(format!("CXX_{}", target.triple), &cxx) - .env(format!("CXXFLAGS_{}", target.triple), cflags); + .env(format!("CXXFLAGS_{}", target.triple), cxxflags); } } @@ -1626,9 +1718,10 @@ impl<'a> Builder<'a> { pub(crate) fn ensure_if_default>>( &'a self, step: S, + kind: Kind, ) -> S::Output { - let desc = StepDescription::from::(); - let should_run = (desc.should_run)(ShouldRun::new(self)); + let desc = StepDescription::from::(kind); + let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind)); // Avoid running steps contained in --exclude for pathset in &should_run.paths { @@ -1642,13 +1735,16 @@ impl<'a> Builder<'a> { } /// Checks if any of the "should_run" paths is in the `Builder` paths. - pub(crate) fn was_invoked_explicitly(&'a self) -> bool { - let desc = StepDescription::from::(); - let should_run = (desc.should_run)(ShouldRun::new(self)); + pub(crate) fn was_invoked_explicitly(&'a self, kind: Kind) -> bool { + let desc = StepDescription::from::(kind); + let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind)); for path in &self.paths { - if should_run.paths.iter().any(|s| s.has(path)) - && !desc.is_excluded(self, &PathSet::Suite(path.clone())) + if should_run.paths.iter().any(|s| s.has(path, Some(desc.kind))) + && !desc.is_excluded( + self, + &PathSet::Suite(TaskPath { path: path.clone(), kind: Some(desc.kind.into()) }), + ) { return true; } diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index bb3ea04d4a..bc71034496 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -499,7 +499,7 @@ mod dist { let host = TargetSelection::from_user("A"); builder.run_step_descriptions( - &[StepDescription::from::()], + &[StepDescription::from::(Kind::Test)], &["library/std".into()], ); @@ -520,7 +520,7 @@ mod dist { #[test] fn test_exclude() { let mut config = configure(&["A"], &["A"]); - config.exclude = vec!["src/tools/tidy".into()]; + config.exclude = vec![TaskPath::parse("src/tools/tidy")]; config.cmd = Subcommand::Test { paths: Vec::new(), test_args: Vec::new(), diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index e750c2963d..8c47f625d7 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -29,7 +29,7 @@ use std::{env, iter}; use build_helper::output; use crate::config::{Target, TargetSelection}; -use crate::{Build, GitRepo}; +use crate::{Build, CLang, GitRepo}; // The `cc` crate doesn't provide a way to obtain a path to the detected archiver, // so use some simplified logic here. First we respect the environment variable `AR`, then @@ -109,7 +109,7 @@ pub fn find(build: &mut Build) { }; build.cc.insert(target, compiler.clone()); - let cflags = build.cflags(target, GitRepo::Rustc); + let cflags = build.cflags(target, GitRepo::Rustc, CLang::C); // If we use llvm-libunwind, we will need a C++ compiler as well for all targets // We'll need one anyways if the target triple is also a host triple @@ -142,8 +142,9 @@ pub fn find(build: &mut Build) { build.verbose(&format!("CC_{} = {:?}", &target.triple, build.cc(target))); build.verbose(&format!("CFLAGS_{} = {:?}", &target.triple, cflags)); if let Ok(cxx) = build.cxx(target) { + let cxxflags = build.cflags(target, GitRepo::Rustc, CLang::Cxx); build.verbose(&format!("CXX_{} = {:?}", &target.triple, cxx)); - build.verbose(&format!("CXXFLAGS_{} = {:?}", &target.triple, cflags)); + build.verbose(&format!("CXXFLAGS_{} = {:?}", &target.triple, cxxflags)); } if let Some(ar) = ar { build.verbose(&format!("AR_{} = {:?}", &target.triple, ar)); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 043b38ecec..f05d1dcf4f 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -29,7 +29,7 @@ use crate::native; use crate::tool::SourceType; use crate::util::{exe, is_debug_info, is_dylib, symlink_dir}; use crate::LLVM_TOOLS; -use crate::{Compiler, DependencyType, GitRepo, Mode}; +use crate::{CLang, Compiler, DependencyType, GitRepo, Mode}; #[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)] pub struct Std { @@ -227,8 +227,10 @@ fn copy_self_contained_objects( target_deps.push((target, DependencyType::TargetSelfContained)); } - let libunwind_path = copy_llvm_libunwind(builder, target, &libdir_self_contained); - target_deps.push((libunwind_path, DependencyType::TargetSelfContained)); + if !target.starts_with("s390x") { + let libunwind_path = copy_llvm_libunwind(builder, target, &libdir_self_contained); + target_deps.push((libunwind_path, DependencyType::TargetSelfContained)); + } } else if target.ends_with("-wasi") { let srcdir = builder .wasi_root(target) @@ -248,7 +250,7 @@ fn copy_self_contained_objects( } } else if target.contains("windows-gnu") { for obj in ["crt2.o", "dllcrt2.o"].iter() { - let src = compiler_file(builder, builder.cc(target), target, obj); + let src = compiler_file(builder, builder.cc(target), target, CLang::C, obj); let target = libdir_self_contained.join(obj); builder.copy(&src, &target); target_deps.push((target, DependencyType::TargetSelfContained)); @@ -722,7 +724,13 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS && !target.contains("msvc") && !target.contains("apple") { - let file = compiler_file(builder, builder.cxx(target).unwrap(), target, "libstdc++.a"); + let file = compiler_file( + builder, + builder.cxx(target).unwrap(), + target, + CLang::Cxx, + "libstdc++.a", + ); cargo.env("LLVM_STATIC_STDCPP", file); } if builder.config.llvm_link_shared { @@ -943,10 +951,11 @@ pub fn compiler_file( builder: &Builder<'_>, compiler: &Path, target: TargetSelection, + c: CLang, file: &str, ) -> PathBuf { let mut cmd = Command::new(compiler); - cmd.args(builder.cflags(target, GitRepo::Rustc)); + cmd.args(builder.cflags(target, GitRepo::Rustc, c)); cmd.arg(format!("-print-file-name={}", file)); let out = output(&mut cmd); PathBuf::from(out.trim()) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 5af9248583..d6f77fe6cd 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -12,6 +12,7 @@ use std::fs; use std::path::{Path, PathBuf}; use std::str::FromStr; +use crate::builder::TaskPath; use crate::cache::{Interned, INTERNER}; use crate::channel::GitInfo; pub use crate::flags::Subcommand; @@ -62,7 +63,7 @@ pub struct Config { pub sanitizers: bool, pub profiler: bool, pub ignore_git: bool, - pub exclude: Vec, + pub exclude: Vec, pub include_default_paths: bool, pub rustc_error_format: Option, pub json_output: bool, @@ -107,6 +108,7 @@ pub struct Config { pub llvm_polly: bool, pub llvm_clang: bool, pub llvm_from_ci: bool, + pub llvm_build_config: HashMap, pub use_lld: bool, pub lld_enabled: bool, @@ -476,6 +478,7 @@ derive_merge! { polly: Option, clang: Option, download_ci_llvm: Option, + build_config: Option>, } } @@ -635,7 +638,7 @@ impl Config { let flags = Flags::parse(&args); let mut config = Config::default_opts(); - config.exclude = flags.exclude; + config.exclude = flags.exclude.into_iter().map(|path| TaskPath::parse(path)).collect(); config.include_default_paths = flags.include_default_paths; config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; @@ -806,6 +809,7 @@ impl Config { config.llvm_allow_old_toolchain = llvm.allow_old_toolchain.unwrap_or(false); config.llvm_polly = llvm.polly.unwrap_or(false); config.llvm_clang = llvm.clang.unwrap_or(false); + config.llvm_build_config = llvm.build_config.clone().unwrap_or(Default::default()); config.llvm_from_ci = match llvm.download_ci_llvm { Some(StringOrBool::String(s)) => { assert!(s == "if-available", "unknown option `{}` for download-ci-llvm", s); @@ -875,6 +879,7 @@ impl Config { check_ci_llvm!(llvm.allow_old_toolchain); check_ci_llvm!(llvm.polly); check_ci_llvm!(llvm.clang); + check_ci_llvm!(llvm.build_config); check_ci_llvm!(llvm.plugins); // CI-built LLVM can be either dynamic or static. diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 7d9b3da48e..f5ae8103cb 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -16,7 +16,7 @@ use std::process::Command; use build_helper::{output, t}; -use crate::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; use crate::compile; use crate::config::TargetSelection; @@ -24,7 +24,6 @@ use crate::tarball::{GeneratedTarball, OverlayKind, Tarball}; use crate::tool::{self, Tool}; use crate::util::{exe, is_dylib, timeit}; use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS}; -use time::{self, Timespec}; pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { format!("{}-{}", component, builder.rust_package_vers()) @@ -422,33 +421,15 @@ impl Step for Rustc { let man_src = builder.src.join("src/doc/man"); let man_dst = image.join("share/man/man1"); - // Reproducible builds: If SOURCE_DATE_EPOCH is set, use that as the time. - let time = env::var("SOURCE_DATE_EPOCH") - .map(|timestamp| { - let epoch = timestamp - .parse() - .map_err(|err| format!("could not parse SOURCE_DATE_EPOCH: {}", err)) - .unwrap(); - - time::at(Timespec::new(epoch, 0)) - }) - .unwrap_or_else(|_| time::now()); - - let month_year = t!(time::strftime("%B %Y", &time)); // don't use our `bootstrap::util::{copy, cp_r}`, because those try // to hardlink, and we don't want to edit the source templates for file_entry in builder.read_dir(&man_src) { let page_src = file_entry.path(); let page_dst = man_dst.join(file_entry.file_name()); + let src_text = t!(std::fs::read_to_string(&page_src)); + let new_text = src_text.replace("", &builder.version); + t!(std::fs::write(&page_dst, &new_text)); t!(fs::copy(&page_src, &page_dst)); - // template in month/year and version number - builder.replace_in_file( - &page_dst, - &[ - ("", &month_year), - ("", &builder.version), - ], - ); } // Debugger scripts @@ -753,6 +734,8 @@ fn copy_src_dirs( "llvm-project\\llvm", "llvm-project/compiler-rt", "llvm-project\\compiler-rt", + "llvm-project/cmake", + "llvm-project\\cmake", ]; if spath.contains("llvm-project") && !spath.ends_with("llvm-project") @@ -1040,6 +1023,12 @@ impl Step for Rls { let rls = builder .ensure(tool::Rls { compiler, target, extra_features: Vec::new() }) .or_else(|| { + if builder.config.rustc_parallel { + // FIXME: Disable RLS on parallel builds, cannot build due + // to upstream trouble. See + // https://github.com/racer-rust/racer/pull/1177. + return None; + } missing_tool("RLS", builder.build.config.missing_tools); None })?; @@ -1368,7 +1357,7 @@ impl Step for Extended { let mut built_tools = HashSet::new(); macro_rules! add_component { ($name:expr => $step:expr) => { - if let Some(tarball) = builder.ensure_if_default($step) { + if let Some(tarball) = builder.ensure_if_default($step, Kind::Dist) { tarballs.push(tarball); built_tools.insert($name); } @@ -1483,11 +1472,10 @@ impl Step for Extended { }; prepare("rustc"); prepare("cargo"); - prepare("rust-docs"); prepare("rust-std"); prepare("rust-analysis"); prepare("clippy"); - for tool in &["rust-demangler", "rls", "rust-analyzer", "miri"] { + for tool in &["rust-docs", "rust-demangler", "rls", "rust-analyzer", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1895,12 +1883,6 @@ fn add_env(builder: &Builder<'_>, cmd: &mut Command, target: TargetSelection) { } else { cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC"); } - - if target.contains("x86_64") { - cmd.env("CFG_PLATFORM", "x64"); - } else { - cmd.env("CFG_PLATFORM", "x86"); - } } /// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking. diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index f0f31c447b..23b5ddcd47 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -15,7 +15,7 @@ use std::path::{Path, PathBuf}; use crate::Mode; use build_helper::{t, up_to_date}; -use crate::builder::{Builder, Compiler, RunConfig, ShouldRun, Step}; +use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; use crate::compile; use crate::config::{Config, TargetSelection}; @@ -240,7 +240,7 @@ impl Step for TheBook { invoke_rustdoc(builder, compiler, target, path); } - if builder.was_invoked_explicitly::() { + if builder.was_invoked_explicitly::(Kind::Doc) { let out = builder.doc_out(target); let index = out.join("book").join("index.html"); open(builder, &index); @@ -400,7 +400,7 @@ impl Step for Standalone { // We open doc/index.html as the default if invoked as `x.py doc --open` // with no particular explicit doc requested (e.g. library/core). - if builder.paths.is_empty() || builder.was_invoked_explicitly::() { + if builder.paths.is_empty() || builder.was_invoked_explicitly::(Kind::Doc) { let index = out.join("index.html"); open(builder, &index); } @@ -902,7 +902,7 @@ impl Step for RustcBook { name: INTERNER.intern_str("rustc"), src: INTERNER.intern_path(out_base), }); - if builder.was_invoked_explicitly::() { + if builder.was_invoked_explicitly::(Kind::Doc) { let out = builder.doc_out(self.target); let index = out.join("rustc").join("index.html"); open(builder, &index); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 8569089f70..3130dcc277 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -106,8 +106,7 @@ use std::cell::{Cell, RefCell}; use std::collections::{HashMap, HashSet}; use std::env; -use std::fs::{self, File, OpenOptions}; -use std::io::{Read, Seek, SeekFrom, Write}; +use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::process::{self, Command}; use std::str; @@ -339,6 +338,11 @@ impl Mode { } } +pub enum CLang { + C, + Cxx, +} + impl Build { /// Creates a new set of build configuration from the `flags` on the command /// line and the filesystem `config`. @@ -527,7 +531,7 @@ impl Build { // Try passing `--progress` to start, then run git again without if that fails. let update = |progress: bool| { let mut git = Command::new("git"); - git.args(&["submodule", "update", "--init", "--recursive"]); + git.args(&["submodule", "update", "--init", "--recursive", "--depth=1"]); if progress { git.arg("--progress"); } @@ -851,7 +855,7 @@ impl Build { return; } self.verbose(&format!("running: {:?}", cmd)); - run(cmd) + run(cmd, self.is_verbose()) } /// Runs a command, printing out nice contextual information if it fails. @@ -871,7 +875,7 @@ impl Build { return true; } self.verbose(&format!("running: {:?}", cmd)); - try_run(cmd) + try_run(cmd, self.is_verbose()) } /// Runs a command, printing out nice contextual information if it fails. @@ -941,10 +945,15 @@ impl Build { /// Returns a list of flags to pass to the C compiler for the target /// specified. - fn cflags(&self, target: TargetSelection, which: GitRepo) -> Vec { + fn cflags(&self, target: TargetSelection, which: GitRepo, c: CLang) -> Vec { + let base = match c { + CLang::C => &self.cc[&target], + CLang::Cxx => &self.cxx[&target], + }; + // Filter out -O and /O (the optimization flags) that we picked up from // cc-rs because the build scripts will determine that for themselves. - let mut base = self.cc[&target] + let mut base = base .args() .iter() .map(|s| s.to_string_lossy().into_owned()) @@ -1335,23 +1344,6 @@ impl Build { } } - /// Search-and-replaces within a file. (Not maximally efficiently: allocates a - /// new string for each replacement.) - pub fn replace_in_file(&self, path: &Path, replacements: &[(&str, &str)]) { - if self.config.dry_run { - return; - } - let mut contents = String::new(); - let mut file = t!(OpenOptions::new().read(true).write(true).open(path)); - t!(file.read_to_string(&mut contents)); - for &(target, replacement) in replacements { - contents = contents.replace(target, replacement); - } - t!(file.seek(SeekFrom::Start(0))); - t!(file.set_len(0)); - t!(file.write_all(contents.as_bytes())); - } - /// Copies the `src` directory recursively to `dst`. Both are assumed to exist /// when this function is called. pub fn cp_r(&self, src: &Path, dst: &Path) { diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 4a754e6da1..8b13fb721d 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -21,7 +21,7 @@ use build_helper::{output, t}; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::config::TargetSelection; use crate::util::{self, exe}; -use crate::GitRepo; +use crate::{CLang, GitRepo}; use build_helper::up_to_date; pub struct Meta { @@ -31,6 +31,29 @@ pub struct Meta { root: String, } +// Linker flags to pass to LLVM's CMake invocation. +#[derive(Debug, Clone, Default)] +struct LdFlags { + // CMAKE_EXE_LINKER_FLAGS + exe: OsString, + // CMAKE_SHARED_LINKER_FLAGS + shared: OsString, + // CMAKE_MODULE_LINKER_FLAGS + module: OsString, +} + +impl LdFlags { + fn push_all(&mut self, s: impl AsRef) { + let s = s.as_ref(); + self.exe.push(" "); + self.exe.push(s); + self.shared.push(" "); + self.shared.push(s); + self.module.push(" "); + self.module.push(s); + } +} + // This returns whether we've already previously built LLVM. // // It's used to avoid busting caches during x.py check -- if we've already built @@ -146,6 +169,7 @@ impl Step for Llvm { // https://llvm.org/docs/CMake.html let mut cfg = cmake::Config::new(builder.src.join(root)); + let mut ldflags = LdFlags::default(); let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) { (false, _) => "Debug", @@ -238,18 +262,7 @@ impl Step for Llvm { cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); } - // For distribution we want the LLVM tools to be *statically* linked to libstdc++ - if builder.config.llvm_tools_enabled { - if !target.contains("msvc") { - if target.contains("apple") { - cfg.define("CMAKE_EXE_LINKER_FLAGS", "-static-libstdc++"); - } else { - cfg.define("CMAKE_EXE_LINKER_FLAGS", "-Wl,-Bsymbolic -static-libstdc++"); - } - } - } - - if !target.contains("freebsd") && target.starts_with("riscv") { + if target.starts_with("riscv") && !target.contains("freebsd") { // RISC-V GCC erroneously requires linking against // `libatomic` when using 1-byte and 2-byte C++ // atomics but the LLVM build system check cannot @@ -257,12 +270,8 @@ impl Step for Llvm { // FreeBSD uses Clang as its system compiler and // provides no libatomic in its base system so does // not want this. - if !builder.config.llvm_tools_enabled { - cfg.define("CMAKE_EXE_LINKER_FLAGS", "-latomic"); - } else { - cfg.define("CMAKE_EXE_LINKER_FLAGS", "-latomic -static-libstdc++"); - } - cfg.define("CMAKE_SHARED_LINKER_FLAGS", "-latomic"); + ldflags.exe.push(" -latomic"); + ldflags.shared.push(" -latomic"); } if target.contains("msvc") { @@ -309,7 +318,7 @@ impl Step for Llvm { // Workaround for ppc32 lld limitation if target == "powerpc-unknown-freebsd" { - cfg.define("CMAKE_EXE_LINKER_FLAGS", "-fuse-ld=bfd"); + ldflags.exe.push(" -fuse-ld=bfd"); } // https://llvm.org/docs/HowToCrossCompileLLVM.html @@ -351,7 +360,11 @@ impl Step for Llvm { cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES"); } - configure_cmake(builder, target, &mut cfg, true); + configure_cmake(builder, target, &mut cfg, true, ldflags); + + for (key, val) in &builder.config.llvm_build_config { + cfg.define(key, val); + } // FIXME: we don't actually need to build all LLVM tools and all LLVM // libraries here, e.g., we just want a few components and a few @@ -395,6 +408,7 @@ fn configure_cmake( target: TargetSelection, cfg: &mut cmake::Config, use_compiler_launcher: bool, + mut ldflags: LdFlags, ) { // Do not print installation messages for up-to-date files. // LLVM and LLD builds can produce a lot of those and hit CI limits on log size. @@ -503,31 +517,30 @@ fn configure_cmake( } cfg.build_arg("-j").build_arg(builder.jobs().to_string()); - let mut cflags = builder.cflags(target, GitRepo::Llvm).join(" "); + let mut cflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::C).join(" ").into(); if let Some(ref s) = builder.config.llvm_cflags { - cflags.push_str(&format!(" {}", s)); + cflags.push(" "); + cflags.push(s); } // Some compiler features used by LLVM (such as thread locals) will not work on a min version below iOS 10. if target.contains("apple-ios") { if target.contains("86-") { - cflags.push_str(" -miphonesimulator-version-min=10.0"); + cflags.push(" -miphonesimulator-version-min=10.0"); } else { - cflags.push_str(" -miphoneos-version-min=10.0"); + cflags.push(" -miphoneos-version-min=10.0"); } } if builder.config.llvm_clang_cl.is_some() { - cflags.push_str(&format!(" --target={}", target)) + cflags.push(&format!(" --target={}", target)); } cfg.define("CMAKE_C_FLAGS", cflags); - let mut cxxflags = builder.cflags(target, GitRepo::Llvm).join(" "); - if builder.config.llvm_static_stdcpp && !target.contains("msvc") && !target.contains("netbsd") { - cxxflags.push_str(" -static-libstdc++"); - } + let mut cxxflags: OsString = builder.cflags(target, GitRepo::Llvm, CLang::Cxx).join(" ").into(); if let Some(ref s) = builder.config.llvm_cxxflags { - cxxflags.push_str(&format!(" {}", s)); + cxxflags.push(" "); + cxxflags.push(s); } if builder.config.llvm_clang_cl.is_some() { - cxxflags.push_str(&format!(" --target={}", target)) + cxxflags.push(&format!(" --target={}", target)); } cfg.define("CMAKE_CXX_FLAGS", cxxflags); if let Some(ar) = builder.ar(target) { @@ -546,17 +559,45 @@ fn configure_cmake( } } - if let Some(ref s) = builder.config.llvm_ldflags { - cfg.define("CMAKE_SHARED_LINKER_FLAGS", s); - cfg.define("CMAKE_MODULE_LINKER_FLAGS", s); - cfg.define("CMAKE_EXE_LINKER_FLAGS", s); + if let Some(ref flags) = builder.config.llvm_ldflags { + ldflags.push_all(flags); + } + + if let Some(flags) = get_var("LDFLAGS", &builder.config.build.triple, &target.triple) { + ldflags.push_all(&flags); + } + + // For distribution we want the LLVM tools to be *statically* linked to libstdc++. + // We also do this if the user explicitly requested static libstdc++. + if builder.config.llvm_tools_enabled || builder.config.llvm_static_stdcpp { + if !target.contains("msvc") && !target.contains("netbsd") { + if target.contains("apple") { + ldflags.push_all("-static-libstdc++"); + } else { + ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++"); + } + } } + cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared); + cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module); + cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe); + if env::var_os("SCCACHE_ERROR_LOG").is_some() { cfg.env("RUSTC_LOG", "sccache=warn"); } } +// Adapted from https://github.com/alexcrichton/cc-rs/blob/fba7feded71ee4f63cfe885673ead6d7b4f2f454/src/lib.rs#L2347-L2365 +fn get_var(var_base: &str, host: &str, target: &str) -> Option { + let kind = if host == target { "HOST" } else { "TARGET" }; + let target_u = target.replace("-", "_"); + env::var_os(&format!("{}_{}", var_base, target)) + .or_else(|| env::var_os(&format!("{}_{}", var_base, target_u))) + .or_else(|| env::var_os(&format!("{}_{}", kind, var_base))) + .or_else(|| env::var_os(var_base)) +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Lld { pub target: TargetSelection, @@ -594,7 +635,7 @@ impl Step for Lld { t!(fs::create_dir_all(&out_dir)); let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld")); - configure_cmake(builder, target, &mut cfg, true); + configure_cmake(builder, target, &mut cfg, true, LdFlags::default()); // This is an awful, awful hack. Discovered when we migrated to using // clang-cl to compile LLVM/LLD it turns out that LLD, when built out of @@ -784,7 +825,7 @@ impl Step for Sanitizers { // Unfortunately sccache currently lacks support to build them successfully. // Disable compiler launcher on Darwin targets to avoid potential issues. let use_compiler_launcher = !self.target.contains("apple-darwin"); - configure_cmake(builder, self.target, &mut cfg, use_compiler_launcher); + configure_cmake(builder, self.target, &mut cfg, use_compiler_launcher, LdFlags::default()); t!(fs::create_dir_all(&out_dir)); cfg.out_dir(out_dir); diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 5bc0a505bf..9a9ef0b769 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -1,7 +1,9 @@ use crate::TargetSelection; use crate::{t, VERSION}; +use std::env::consts::EXE_SUFFIX; use std::fmt::Write as _; -use std::path::{Path, PathBuf}; +use std::fs::File; +use std::path::{Path, PathBuf, MAIN_SEPARATOR}; use std::process::Command; use std::str::FromStr; use std::{ @@ -109,7 +111,8 @@ pub fn setup(src_path: &Path, profile: Profile) { println!("`x.py` will now use the configuration at {}", include_path.display()); let build = TargetSelection::from_user(&env!("BUILD_TRIPLE")); - let stage_path = ["build", build.rustc_target_arg(), "stage1"].join("/"); + let stage_path = + ["build", build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string()); println!(); @@ -171,6 +174,13 @@ fn attempt_toolchain_link(stage_path: &str) { return; } + if !ensure_stage1_toolchain_placeholder_exists(stage_path) { + println!( + "Failed to create a template for stage 1 toolchain or confirm that it already exists" + ); + return; + } + if try_link_toolchain(&stage_path[..]) { println!( "Added `stage1` rustup toolchain; try `cargo +stage1 build` on a separate rust project to run a newly-built toolchain" @@ -219,6 +229,33 @@ fn try_link_toolchain(stage_path: &str) -> bool { .map_or(false, |output| output.status.success()) } +fn ensure_stage1_toolchain_placeholder_exists(stage_path: &str) -> bool { + let pathbuf = PathBuf::from(stage_path); + + if fs::create_dir_all(pathbuf.join("lib")).is_err() { + return false; + }; + + let pathbuf = pathbuf.join("bin"); + if fs::create_dir_all(&pathbuf).is_err() { + return false; + }; + + let pathbuf = pathbuf.join(format!("rustc{}", EXE_SUFFIX)); + + if pathbuf.exists() { + return true; + } + + // Take care not to overwrite the file + let result = File::options().append(true).create(true).open(&pathbuf); + if result.is_err() { + return false; + } + + return true; +} + // Used to get the path for `Subcommand::Setup` pub fn interactive_path() -> io::Result { fn abbrev_all() -> impl Iterator { @@ -271,9 +308,9 @@ fn install_git_hook_maybe(src_path: &Path) -> io::Result<()> { let mut input = String::new(); println!( "Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality. -If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` on each commit -to ensure your code is up to par. If you decide later that this behavior is undesirable, -simply delete the `pre-commit` file from .git/hooks." +If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` before +pushing your code to ensure your code is up to par. If you decide later that this behavior is +undesirable, simply delete the `pre-push` file from .git/hooks." ); let should_install = loop { @@ -293,21 +330,21 @@ simply delete the `pre-commit` file from .git/hooks." }; if should_install { - let src = src_path.join("src").join("etc").join("pre-commit.sh"); + let src = src_path.join("src").join("etc").join("pre-push.sh"); let git = t!(Command::new("git").args(&["rev-parse", "--git-common-dir"]).output().map( |output| { assert!(output.status.success(), "failed to run `git`"); PathBuf::from(t!(String::from_utf8(output.stdout)).trim()) } )); - let dst = git.join("hooks").join("pre-commit"); + let dst = git.join("hooks").join("pre-push"); match fs::hard_link(src, &dst) { Err(e) => println!( "error: could not create hook {}: do you already have the git hook installed?\n{}", dst.display(), e ), - Ok(_) => println!("Linked `src/etc/pre-commit.sh` to `.git/hooks/pre-commit`"), + Ok(_) => println!("Linked `src/etc/pre-commit.sh` to `.git/hooks/pre-push`"), }; } else { println!("Ok, skipping installation!"); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index cb1b0ebf8d..19d98df3ce 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -24,7 +24,7 @@ use crate::tool::{self, SourceType, Tool}; use crate::toolstate::ToolState; use crate::util::{self, add_link_lib_path, dylib_path, dylib_path_var}; use crate::Crate as CargoCrate; -use crate::{envify, DocTests, GitRepo, Mode}; +use crate::{envify, CLang, DocTests, GitRepo, Mode}; const ADB_TEST_DIR: &str = "/data/tmp/work"; @@ -1509,7 +1509,9 @@ note: if you're sure you want to do this, please open an issue as to why. In the .arg("--cxx") .arg(builder.cxx(target).unwrap()) .arg("--cflags") - .arg(builder.cflags(target, GitRepo::Rustc).join(" ")); + .arg(builder.cflags(target, GitRepo::Rustc, CLang::C).join(" ")) + .arg("--cxxflags") + .arg(builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ")); copts_passed = true; if let Some(ar) = builder.ar(target) { cmd.arg("--ar").arg(ar); @@ -1520,7 +1522,14 @@ note: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--llvm-components").arg(""); } if !copts_passed { - cmd.arg("--cc").arg("").arg("--cxx").arg("").arg("--cflags").arg(""); + cmd.arg("--cc") + .arg("") + .arg("--cxx") + .arg("") + .arg("--cflags") + .arg("") + .arg("--cxxflags") + .arg(""); } if builder.remote_tested(target) { @@ -1540,6 +1549,9 @@ note: if you're sure you want to do this, please open an issue as to why. In the } } cmd.env("RUSTC_BOOTSTRAP", "1"); + // Override the rustc version used in symbol hashes to reduce the amount of normalization + // needed when diffing test output. + cmd.env("RUSTC_FORCE_RUSTC_VERSION", "compiletest"); cmd.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel()); builder.add_rust_test_threads(&mut cmd); diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index ee58bedcc8..2c78ceb1e5 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -282,9 +282,10 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef>( if !path.starts_with(suite_path) { return None; } - let exists = path.is_dir() || path.is_file(); + let abs_path = builder.src.join(path); + let exists = abs_path.is_dir() || abs_path.is_file(); if !exists { - if let Some(p) = path.to_str() { + if let Some(p) = abs_path.to_str() { builder.info(&format!("Warning: Skipping \"{}\": not a regular file or directory", p)); } return None; diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index b1ec072f3f..24aded5473 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs @@ -55,24 +55,18 @@ pub fn restore_library_path() { } } -/// Run the command, printing what we are running. -pub fn run_verbose(cmd: &mut Command) { - println!("running: {:?}", cmd); - run(cmd); -} - -pub fn run(cmd: &mut Command) { - if !try_run(cmd) { +pub fn run(cmd: &mut Command, print_cmd_on_fail: bool) { + if !try_run(cmd, print_cmd_on_fail) { std::process::exit(1); } } -pub fn try_run(cmd: &mut Command) -> bool { +pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool { let status = match cmd.status() { Ok(status) => status, Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)), }; - if !status.success() { + if !status.success() && print_cmd_on_fail { println!( "\n\ncommand did not execute successfully: {:?}\n\ expected success, got: {}\n\n", @@ -108,16 +102,6 @@ pub fn try_run_suppressed(cmd: &mut Command) -> bool { output.status.success() } -pub fn gnu_target(target: &str) -> &str { - match target { - "i686-pc-windows-msvc" => "i686-pc-win32", - "x86_64-pc-windows-msvc" => "x86_64-pc-win32", - "i686-pc-windows-gnu" => "i686-w64-mingw32", - "x86_64-pc-windows-gnu" => "x86_64-w64-mingw32", - s => s, - } -} - pub fn make(host: &str) -> PathBuf { if host.contains("dragonfly") || host.contains("freebsd") diff --git a/src/ci/cpu-usage-over-time.py b/src/ci/cpu-usage-over-time.py index 267c3964d0..adfd895ead 100644 --- a/src/ci/cpu-usage-over-time.py +++ b/src/ci/cpu-usage-over-time.py @@ -108,37 +108,37 @@ elif sys.platform == 'darwin': from ctypes import * libc = cdll.LoadLibrary('/usr/lib/libc.dylib') - PROESSOR_CPU_LOAD_INFO = c_int(2) + class host_cpu_load_info_data_t(Structure): + _fields_ = [("cpu_ticks", c_uint * 4)] + + host_statistics = libc.host_statistics + host_statistics.argtypes = [ + c_uint, + c_int, + POINTER(host_cpu_load_info_data_t), + POINTER(c_int) + ] + host_statistics.restype = c_int + CPU_STATE_USER = 0 CPU_STATE_SYSTEM = 1 CPU_STATE_IDLE = 2 CPU_STATE_NICE = 3 - c_int_p = POINTER(c_int) - class State: def __init__(self): - num_cpus_u = c_uint(0) - cpu_info = c_int_p() - cpu_info_cnt = c_int(0) - err = libc.host_processor_info( + stats = host_cpu_load_info_data_t() + count = c_int(4) # HOST_CPU_LOAD_INFO_COUNT + err = libc.host_statistics( libc.mach_host_self(), - PROESSOR_CPU_LOAD_INFO, - byref(num_cpus_u), - byref(cpu_info), - byref(cpu_info_cnt), + c_int(3), # HOST_CPU_LOAD_INFO + byref(stats), + byref(count), ) assert err == 0 - self.user = 0 - self.system = 0 - self.idle = 0 - self.nice = 0 - cur = 0 - while cur < cpu_info_cnt.value: - self.user += cpu_info[cur + CPU_STATE_USER] - self.system += cpu_info[cur + CPU_STATE_SYSTEM] - self.idle += cpu_info[cur + CPU_STATE_IDLE] - self.nice += cpu_info[cur + CPU_STATE_NICE] - cur += num_cpus_u.value + self.system = stats.cpu_ticks[CPU_STATE_SYSTEM] + self.user = stats.cpu_ticks[CPU_STATE_USER] + self.idle = stats.cpu_ticks[CPU_STATE_IDLE] + self.nice = stats.cpu_ticks[CPU_STATE_NICE] def idle_since(self, prev): user = self.user - prev.user diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile index ee4fd759b4..79c2c1d93d 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile @@ -1,17 +1,4 @@ FROM ubuntu:20.04 -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - curl \ - ca-certificates -WORKDIR /tmp -RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem - -FROM ubuntu:16.04 - -# The ca-certificates in ubuntu-16 is too old, so update the certificates -# with something more recent. -COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem -ENV CURL_CA_BUNDLE /tmp/cacert.pem COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh diff --git a/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile index b11a1d3feb..dd1c83b413 100644 --- a/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-armhf-linux/Dockerfile @@ -1,17 +1,4 @@ FROM ubuntu:20.04 -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - curl \ - ca-certificates -WORKDIR /tmp -RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem - -FROM ubuntu:16.04 - -# The ca-certificates in ubuntu-16 is too old, so update the certificates -# with something more recent. -COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem -ENV CURL_CA_BUNDLE /tmp/cacert.pem COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh diff --git a/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile index 55ca23b293..32e3bc22ad 100644 --- a/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-armv7-linux/Dockerfile @@ -1,17 +1,4 @@ FROM ubuntu:20.04 -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - curl \ - ca-certificates -WORKDIR /tmp -RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem - -FROM ubuntu:16.04 - -# The ca-certificates in ubuntu-16 is too old, so update the certificates -# with something more recent. -COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem -ENV CURL_CA_BUNDLE /tmp/cacert.pem COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh diff --git a/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile index ef49904b53..51645a8185 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-musl/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++ \ make \ ninja-build \ diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index c27e42a266..66333e2b99 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -17,12 +17,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config \ mingw-w64 -RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | tar -xJ -ENV PATH="/node-v14.4.0-linux-x64/bin:${PATH}" +RUN curl -sL https://nodejs.org/dist/v16.9.0/node-v16.9.0-linux-x64.tar.xz | tar -xJ +ENV PATH="/node-v16.9.0-linux-x64/bin:${PATH}" # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. -RUN npm install es-check@5.2.3 -g -RUN npm install eslint@7.20.0 -g +RUN npm install es-check@6.1.1 -g +RUN npm install eslint@8.6.0 -g COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -40,5 +40,5 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ /scripts/validate-toolstate.sh && \ /scripts/validate-error-codes.sh && \ # Runs checks to ensure that there are no ES5 issues in our JS code. - es-check es5 ../src/librustdoc/html/static/js/*.js && \ + es-check es6 ../src/librustdoc/html/static/js/*.js && \ eslint ../src/librustdoc/html/static/js/*.js diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index d4701a2561..f1c42b248f 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -72,7 +72,7 @@ ENV PATH="/node-v14.4.0-linux-x64/bin:${PATH}" # https://github.com/puppeteer/puppeteer/issues/375 # # We also specify the version in case we need to update it to go around cache limitations. -RUN npm install -g browser-ui-test@0.5.3 --unsafe-perm=true +RUN npm install -g browser-ui-test@0.7.2 --unsafe-perm=true ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ diff --git a/src/ci/docker/scripts/cmake.sh b/src/ci/docker/scripts/cmake.sh index 7009617340..f124dbdaa6 100755 --- a/src/ci/docker/scripts/cmake.sh +++ b/src/ci/docker/scripts/cmake.sh @@ -5,16 +5,16 @@ hide_output() { set +x on_err=" echo ERROR: An error was encountered with the build. -cat /tmp/build.log +cat /tmp/cmake_build.log exit 1 " trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - "$@" &> /tmp/build.log + "$@" &> /tmp/cmake_build.log trap - ERR kill $PING_LOOP_PID - rm /tmp/build.log + rm /tmp/cmake_build.log set -x } diff --git a/src/ci/docker/scripts/musl-toolchain.sh b/src/ci/docker/scripts/musl-toolchain.sh index 3c17f316d1..e358b8139d 100644 --- a/src/ci/docker/scripts/musl-toolchain.sh +++ b/src/ci/docker/scripts/musl-toolchain.sh @@ -48,7 +48,9 @@ cd musl-cross-make git checkout a54eb56f33f255dfca60be045f12a5cfaf5a72a9 # Fix the cfi detection script in musl's configure so cfi is generated -# when debug info is asked for. +# when debug info is asked for. This patch is derived from +# https://git.musl-libc.org/cgit/musl/commit/?id=c4d4028dde90562f631edf559fbc42d8ec1b29de. +# When we upgrade to a version that includes this commit, we can remove the patch. mkdir patches/musl-1.1.24 cp ../musl-patch-configure.diff patches/musl-1.1.24/0001-fix-cfi-detection.diff diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 1617a74ad2..8abf4244a3 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -452,12 +452,10 @@ jobs: # macOS Builders # #################### - # Only generate documentation for x86_64-apple-darwin, not other - # tier 2 targets produced by this builder. - name: dist-x86_64-apple env: - SCRIPT: ./x.py dist --exclude rust-docs --exclude extended && ./x.py dist --target=x86_64-apple-darwin rust-docs && ./x.py dist extended - RUST_CONFIGURE_ARGS: --host=x86_64-apple-darwin --target=x86_64-apple-darwin,aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false + SCRIPT: ./x.py dist --host=x86_64-apple-darwin --target=x86_64-apple-darwin + RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.7 NO_LLVM_ASSERTIONS: 1 @@ -466,6 +464,17 @@ jobs: DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-macos-xl + - name: dist-apple-various + env: + SCRIPT: ./x.py dist --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim + RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.7 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + NO_OVERFLOW_CHECKS: 1 + <<: *job-macos-xl + - name: dist-x86_64-apple-alt env: SCRIPT: ./x.py dist @@ -477,9 +486,9 @@ jobs: NO_OVERFLOW_CHECKS: 1 <<: *job-macos-xl - - name: x86_64-apple - env: - SCRIPT: ./x.py --stage 2 test + - name: x86_64-apple-1 + env: &env-x86_64-apple-tests + SCRIPT: ./x.py --stage 2 test --exclude src/test/ui --exclude src/test/rustdoc --exclude src/test/run-make-fulldeps RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.8 @@ -489,6 +498,12 @@ jobs: NO_OVERFLOW_CHECKS: 1 <<: *job-macos-xl + - name: x86_64-apple-2 + env: + SCRIPT: ./x.py --stage 2 test src/test/ui src/test/rustdoc src/test/run-make-fulldeps + <<: *env-x86_64-apple-tests + <<: *job-macos-xl + # This target only needs to support 11.0 and up as nothing else supports the hardware - name: dist-aarch64-apple env: @@ -500,6 +515,7 @@ jobs: --enable-full-tools --enable-sanitizers --enable-profiler + --disable-docs --set rust.jemalloc --set llvm.ninja=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 diff --git a/src/ci/pgo.sh b/src/ci/pgo.sh index 29ef13a60f..eaf92b033a 100755 --- a/src/ci/pgo.sh +++ b/src/ci/pgo.sh @@ -14,10 +14,10 @@ python3 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \ --llvm-profile-generate # Profile libcore compilation in opt-level=0 and opt-level=3 -RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \ - --crate-type=lib ../library/core/src/lib.rs -RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \ - --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs +RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \ + --edition=2021 --crate-type=lib ../library/core/src/lib.rs +RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \ + --edition=2021 --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs # Merge the profile data we gathered for LLVM # Note that this uses the profdata from the clang we used to build LLVM, @@ -37,10 +37,10 @@ python3 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \ --rust-profile-generate=/tmp/rustc-pgo # Profile libcore compilation in opt-level=0 and opt-level=3 -RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \ - --crate-type=lib ../library/core/src/lib.rs -RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \ - --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs +RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \ + --edition=2021 --crate-type=lib ../library/core/src/lib.rs +RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \ + --edition=2021 --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs cp -r /tmp/rustc-perf ./ chown -R $(whoami): ./rustc-perf diff --git a/src/doc/book/.github/workflows/main.yml b/src/doc/book/.github/workflows/main.yml index 1acae0b6d2..5504715123 100644 --- a/src/doc/book/.github/workflows/main.yml +++ b/src/doc/book/.github/workflows/main.yml @@ -12,8 +12,8 @@ jobs: - name: Install Rust run: | rustup set profile minimal - rustup toolchain install 1.57 -c rust-docs - rustup default 1.57 + rustup toolchain install 1.58 -c rust-docs + rustup default 1.58 - name: Install mdbook run: | mkdir bin diff --git a/src/doc/book/listings/ch02-guessing-game-tutorial/listing-02-04/output.txt b/src/doc/book/listings/ch02-guessing-game-tutorial/listing-02-04/output.txt index 62fe8327fc..61494725dd 100644 --- a/src/doc/book/listings/ch02-guessing-game-tutorial/listing-02-04/output.txt +++ b/src/doc/book/listings/ch02-guessing-game-tutorial/listing-02-04/output.txt @@ -16,5 +16,30 @@ error[E0308]: mismatched types = note: expected reference `&String` found reference `&{integer}` -For more information about this error, try `rustc --explain E0308`. -error: could not compile `guessing_game` due to previous error +error[E0283]: type annotations needed for `{integer}` + --> src/main.rs:8:44 + | +8 | let secret_number = rand::thread_rng().gen_range(1..101); + | ------------- ^^^^^^^^^ cannot infer type for type `{integer}` + | | + | consider giving `secret_number` a type + | + = note: multiple `impl`s satisfying `{integer}: SampleUniform` found in the `rand` crate: + - impl SampleUniform for i128; + - impl SampleUniform for i16; + - impl SampleUniform for i32; + - impl SampleUniform for i64; + and 8 more +note: required by a bound in `gen_range` + --> /Users/carolnichols/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.3/src/rng.rs:129:12 + | +129 | T: SampleUniform, + | ^^^^^^^^^^^^^ required by this bound in `gen_range` +help: consider specifying the type arguments in the function call + | +8 | let secret_number = rand::thread_rng().gen_range::(1..101); + | ++++++++ + +Some errors have detailed explanations: E0283, E0308. +For more information about an error, try `rustc --explain E0283`. +error: could not compile `guessing_game` due to 2 previous errors diff --git a/src/doc/book/listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt b/src/doc/book/listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt index 8701a07d38..31a07efc4b 100644 --- a/src/doc/book/listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt +++ b/src/doc/book/listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt @@ -3,6 +3,8 @@ $ cargo run error[E0308]: mismatched types --> src/main.rs:3:14 | +2 | let mut spaces = " "; + | ----- expected due to this value 3 | spaces = spaces.len(); | ^^^^^^^^^^^^ expected `&str`, found `usize` diff --git a/src/doc/book/listings/ch09-error-handling/listing-09-10/output.txt b/src/doc/book/listings/ch09-error-handling/listing-09-10/output.txt index 4c36d20285..6a8b1a4c21 100644 --- a/src/doc/book/listings/ch09-error-handling/listing-09-10/output.txt +++ b/src/doc/book/listings/ch09-error-handling/listing-09-10/output.txt @@ -1,16 +1,15 @@ $ cargo run Compiling error-handling v0.1.0 (file:///projects/error-handling) error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> src/main.rs:4:36 - | -3 | / fn main() { -4 | | let f = File::open("hello.txt")?; - | | ^ cannot use the `?` operator in a function that returns `()` -5 | | } - | |_- this function should return `Result` or `Option` to accept `?` - | - = help: the trait `FromResidual>` is not implemented for `()` -note: required by `from_residual` + --> src/main.rs:4:36 + | +3 | / fn main() { +4 | | let f = File::open("hello.txt")?; + | | ^ cannot use the `?` operator in a function that returns `()` +5 | | } + | |_- this function should return `Result` or `Option` to accept `?` + | + = help: the trait `FromResidual>` is not implemented for `()` For more information about this error, try `rustc --explain E0277`. error: could not compile `error-handling` due to previous error diff --git a/src/doc/book/listings/ch09-error-handling/no-listing-03-closures/Cargo.toml b/src/doc/book/listings/ch09-error-handling/no-listing-03-closures/Cargo.toml deleted file mode 100644 index c496db7834..0000000000 --- a/src/doc/book/listings/ch09-error-handling/no-listing-03-closures/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "error-handling" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/src/doc/book/listings/ch09-error-handling/no-listing-03-closures/src/main.rs b/src/doc/book/listings/ch09-error-handling/no-listing-03-closures/src/main.rs deleted file mode 100644 index c6682cd233..0000000000 --- a/src/doc/book/listings/ch09-error-handling/no-listing-03-closures/src/main.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::fs::File; -use std::io::ErrorKind; - -fn main() { - let f = File::open("hello.txt").unwrap_or_else(|error| { - if error.kind() == ErrorKind::NotFound { - File::create("hello.txt").unwrap_or_else(|error| { - panic!("Problem creating the file: {:?}", error); - }) - } else { - panic!("Problem opening the file: {:?}", error); - } - }); -} diff --git a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/Cargo.lock b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/Cargo.lock index 77292f6149..a456055c9d 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/Cargo.lock +++ b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/Cargo.lock @@ -1,13 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "add-one" +name = "add_one" version = "0.1.0" [[package]] name = "adder" version = "0.1.0" dependencies = [ - "add-one 0.1.0", + "add_one 0.1.0", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/Cargo.toml index d26e7cfb4d..1448801d5b 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/Cargo.toml @@ -2,5 +2,5 @@ members = [ "adder", - "add-one", + "add_one", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add-one/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add_one/Cargo.toml similarity index 78% rename from src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add-one/Cargo.toml rename to src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add_one/Cargo.toml index 0262902938..8af4ab8166 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add-one/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add_one/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "add-one" +name = "add_one" version = "0.1.0" edition = "2021" diff --git a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add-one/src/lib.rs b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add_one/src/lib.rs similarity index 100% rename from src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add-one/src/lib.rs rename to src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add_one/src/lib.rs diff --git a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/adder/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/adder/Cargo.toml index ea9e302eea..feb3d956ea 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/adder/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/adder/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" [dependencies] -add-one = { path = "../add-one" } +add_one = { path = "../add_one" } diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.lock b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.lock index 77292f6149..a456055c9d 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.lock +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.lock @@ -1,13 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "add-one" +name = "add_one" version = "0.1.0" [[package]] name = "adder" version = "0.1.0" dependencies = [ - "add-one 0.1.0", + "add_one 0.1.0", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.toml index d26e7cfb4d..1448801d5b 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.toml @@ -2,5 +2,5 @@ members = [ "adder", - "add-one", + "add_one", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add-one/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add-one/Cargo.toml deleted file mode 100644 index 0262902938..0000000000 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add-one/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "add-one" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add_one/Cargo.toml similarity index 78% rename from src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/Cargo.toml rename to src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add_one/Cargo.toml index 9b816e7661..8af4ab8166 100644 --- a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add_one/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "gui" +name = "add_one" version = "0.1.0" edition = "2021" diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add-one/src/lib.rs b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add_one/src/lib.rs similarity index 100% rename from src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add-one/src/lib.rs rename to src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add_one/src/lib.rs diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/adder/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/adder/Cargo.toml index ea9e302eea..55c02036c4 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/adder/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/adder/Cargo.toml @@ -4,5 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] - -add-one = { path = "../add-one" } +add_one = { path = "../add_one" } diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/Cargo.lock b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/Cargo.lock index 28663ec168..eec3a9e4b1 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/Cargo.lock +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/Cargo.lock @@ -1,7 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "add-one" +name = "add_one" version = "0.1.0" dependencies = [ "rand", @@ -11,7 +11,7 @@ dependencies = [ name = "adder" version = "0.1.0" dependencies = [ - "add-one", + "add_one", ] [[package]] diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/Cargo.toml index d26e7cfb4d..1448801d5b 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/Cargo.toml @@ -2,5 +2,5 @@ members = [ "adder", - "add-one", + "add_one", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add-one/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add_one/Cargo.toml similarity index 81% rename from src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add-one/Cargo.toml rename to src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add_one/Cargo.toml index 9e6905425e..fd4942ace4 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add-one/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add_one/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "add-one" +name = "add_one" version = "0.1.0" edition = "2021" diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add-one/src/lib.rs b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add_one/src/lib.rs similarity index 100% rename from src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add-one/src/lib.rs rename to src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add_one/src/lib.rs diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/adder/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/adder/Cargo.toml index ea9e302eea..feb3d956ea 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/adder/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/adder/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" [dependencies] -add-one = { path = "../add-one" } +add_one = { path = "../add_one" } diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/Cargo.lock b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/Cargo.lock index 77292f6149..a456055c9d 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/Cargo.lock +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/Cargo.lock @@ -1,13 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "add-one" +name = "add_one" version = "0.1.0" [[package]] name = "adder" version = "0.1.0" dependencies = [ - "add-one 0.1.0", + "add_one 0.1.0", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/Cargo.toml index d26e7cfb4d..1448801d5b 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/Cargo.toml @@ -2,5 +2,5 @@ members = [ "adder", - "add-one", + "add_one", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add-one/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/Cargo.toml similarity index 78% rename from src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add-one/Cargo.toml rename to src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/Cargo.toml index 0262902938..8af4ab8166 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/listing-14-07/add/add-one/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "add-one" +name = "add_one" version = "0.1.0" edition = "2021" diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add-one/src/lib.rs b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/src/lib.rs similarity index 100% rename from src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add-one/src/lib.rs rename to src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/src/lib.rs diff --git a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/adder/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/adder/Cargo.toml index ea9e302eea..feb3d956ea 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/adder/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/adder/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" [dependencies] -add-one = { path = "../add-one" } +add_one = { path = "../add_one" } diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/Cargo.lock b/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/Cargo.lock index 77292f6149..a456055c9d 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/Cargo.lock +++ b/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/Cargo.lock @@ -1,13 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "add-one" +name = "add_one" version = "0.1.0" [[package]] name = "adder" version = "0.1.0" dependencies = [ - "add-one 0.1.0", + "add_one 0.1.0", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/Cargo.toml index d26e7cfb4d..1448801d5b 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/Cargo.toml @@ -2,5 +2,5 @@ members = [ "adder", - "add-one", + "add_one", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/add-one/src/lib.rs b/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/add-one/src/lib.rs deleted file mode 100644 index 31e1bb209f..0000000000 --- a/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/add-one/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/adder/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/adder/Cargo.toml index ea9e302eea..feb3d956ea 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/adder/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/output-only-02-add-one/add/adder/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" [dependencies] -add-one = { path = "../add-one" } +add_one = { path = "../add_one" } diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/Cargo.lock b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/Cargo.lock index 28663ec168..eec3a9e4b1 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/Cargo.lock +++ b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/Cargo.lock @@ -1,7 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "add-one" +name = "add_one" version = "0.1.0" dependencies = [ "rand", @@ -11,7 +11,7 @@ dependencies = [ name = "adder" version = "0.1.0" dependencies = [ - "add-one", + "add_one", ] [[package]] diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/Cargo.toml index d26e7cfb4d..1448801d5b 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/Cargo.toml @@ -2,5 +2,5 @@ members = [ "adder", - "add-one", + "add_one", ] diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add-one/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add_one/Cargo.toml similarity index 81% rename from src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add-one/Cargo.toml rename to src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add_one/Cargo.toml index 9e6905425e..fd4942ace4 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add-one/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add_one/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "add-one" +name = "add_one" version = "0.1.0" edition = "2021" diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add-one/src/lib.rs b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add_one/src/lib.rs similarity index 100% rename from src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add-one/src/lib.rs rename to src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/add_one/src/lib.rs diff --git a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/adder/Cargo.toml b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/adder/Cargo.toml index ea9e302eea..feb3d956ea 100644 --- a/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/adder/Cargo.toml +++ b/src/doc/book/listings/ch14-more-about-cargo/output-only-03-use-rand/add/adder/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" [dependencies] -add-one = { path = "../add-one" } +add_one = { path = "../add_one" } diff --git a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/Cargo.lock b/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/Cargo.lock deleted file mode 100644 index 00d7b21826..0000000000 --- a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/Cargo.lock +++ /dev/null @@ -1,6 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "gui" -version = "0.1.0" - 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 deleted file mode 100644 index fa60c4a420..0000000000 --- a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/output.txt +++ /dev/null @@ -1,13 +0,0 @@ -$ cargo build - Compiling gui v0.1.0 (file:///projects/gui) -error[E0038]: the trait `Clone` cannot be made into an object - --> src/lib.rs:2:29 - | -2 | pub components: Vec>, - | ^^^^^^^^^ `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 - -For more information about this error, try `rustc --explain E0038`. -error: could not compile `gui` due to previous error diff --git a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/src/lib.rs b/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/src/lib.rs deleted file mode 100644 index e6b1a37f0a..0000000000 --- a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub struct Screen { - pub components: Vec>, -} diff --git a/src/doc/book/listings/ch18-patterns-and-matching/listing-18-26/src/main.rs b/src/doc/book/listings/ch18-patterns-and-matching/listing-18-26/src/main.rs index 4ec86cba76..41fce97950 100644 --- a/src/doc/book/listings/ch18-patterns-and-matching/listing-18-26/src/main.rs +++ b/src/doc/book/listings/ch18-patterns-and-matching/listing-18-26/src/main.rs @@ -3,8 +3,8 @@ fn main() { let num = Some(4); match num { - Some(x) if x < 5 => println!("less than five: {}", x), - Some(x) => println!("{}", x), + Some(x) if x % 2 == 0 => println!("The number {} is even", x), + Some(x) => println!("The number {} is odd", x), None => (), } // ANCHOR_END: here diff --git a/src/doc/book/listings/ch19-advanced-features/listing-19-20/output.txt b/src/doc/book/listings/ch19-advanced-features/listing-19-20/output.txt index 7e3f189f35..6845082452 100644 --- a/src/doc/book/listings/ch19-advanced-features/listing-19-20/output.txt +++ b/src/doc/book/listings/ch19-advanced-features/listing-19-20/output.txt @@ -7,11 +7,6 @@ error[E0283]: type annotations needed | ^^^^^^^^^^^^^^^^^ cannot infer type | = note: cannot satisfy `_: Animal` -note: required by `Animal::baby_name` - --> src/main.rs:2:5 - | -2 | fn baby_name() -> String; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ For more information about this error, try `rustc --explain E0283`. error: could not compile `traits-example` due to previous error diff --git a/src/doc/book/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt b/src/doc/book/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt index 5d621db834..21c726ca90 100644 --- a/src/doc/book/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt +++ b/src/doc/book/listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt @@ -10,13 +10,14 @@ error[E0308]: mismatched types --> src/lib.rs:72:22 | 72 | Worker { id, thread } - | ^^^^^^ - | | - | expected enum `Option`, found struct `JoinHandle` - | help: try using a variant of the expected enum: `Some(thread)` + | ^^^^^^ expected enum `Option`, found struct `JoinHandle` | = note: expected enum `Option>` found struct `JoinHandle<_>` +help: try wrapping the expression in `Some` + | +72 | Worker { id, Some(thread) } + | +++++ + Some errors have detailed explanations: E0308, E0599. For more information about an error, try `rustc --explain E0308`. diff --git a/src/doc/book/nostarch/chapter02.md b/src/doc/book/nostarch/chapter02.md index 6381bbe805..dcd6f256cf 100644 --- a/src/doc/book/nostarch/chapter02.md +++ b/src/doc/book/nostarch/chapter02.md @@ -1,3 +1,8 @@ + [TOC] diff --git a/src/doc/book/nostarch/chapter03.md b/src/doc/book/nostarch/chapter03.md index 2a00baffe4..f116238857 100644 --- a/src/doc/book/nostarch/chapter03.md +++ b/src/doc/book/nostarch/chapter03.md @@ -1,3 +1,8 @@ + [TOC] diff --git a/src/doc/book/nostarch/chapter04.md b/src/doc/book/nostarch/chapter04.md index 8334bf1202..e170c724d7 100644 --- a/src/doc/book/nostarch/chapter04.md +++ b/src/doc/book/nostarch/chapter04.md @@ -1,3 +1,8 @@ + [TOC] diff --git a/src/doc/book/nostarch/chapter05.md b/src/doc/book/nostarch/chapter05.md index d280447a04..121aad61a0 100644 --- a/src/doc/book/nostarch/chapter05.md +++ b/src/doc/book/nostarch/chapter05.md @@ -1,3 +1,8 @@ + [TOC] diff --git a/src/doc/book/nostarch/chapter06.md b/src/doc/book/nostarch/chapter06.md index 47ca417010..6a5eda6573 100644 --- a/src/doc/book/nostarch/chapter06.md +++ b/src/doc/book/nostarch/chapter06.md @@ -1,3 +1,8 @@ + [TOC] diff --git a/src/doc/book/nostarch/chapter08.md b/src/doc/book/nostarch/chapter08.md index 89618120f6..3e808e9175 100644 --- a/src/doc/book/nostarch/chapter08.md +++ b/src/doc/book/nostarch/chapter08.md @@ -1,3 +1,8 @@ + [TOC] @@ -35,7 +40,7 @@ lines of text in a file or the prices of items in a shopping cart. ### Creating a New Vector -To create a new, empty vector, we can call the `Vec::new` function, as shown in +To create a new empty vector, we call the `Vec::new` function, as shown in Listing 8-1. ``` @@ -48,18 +53,18 @@ Note that we added a type annotation here. Because we aren’t inserting any values into this vector, Rust doesn’t know what kind of elements we intend to store. This is an important point. Vectors are implemented using generics; we’ll cover how to use generics with your own types in Chapter 10. For now, -know that the `Vec` type provided by the standard library can hold any type, -and when a specific vector holds a specific type, the type is specified within +know that the `Vec` type provided by the standard library can hold any type. +When we create a vector to hold a specific type, we can specify the type within angle brackets. In Listing 8-1, we’ve told Rust that the `Vec` in `v` will hold elements of the `i32` type. -In more realistic code, Rust can often infer the type of value you want to -store once you insert values, so you rarely need to do this type annotation. -It’s more common to create a `Vec` that has initial values, and Rust -provides the `vec!` macro for convenience. The macro will create a new vector -that holds the values you give it. Listing 8-2 creates a new `Vec` that -holds the values `1`, `2`, and `3`. The integer type is `i32` because that’s -the default integer type, as we discussed in the “Data Types” section of Chapter 3. +More often, you'll create a `Vec` with initial values and Rust will infer +the type of value you want to store, so you rarely need to do this type +annotation. Rust conveniently provides the `vec!` macro, which will create a +new vector that holds the values you give it. Listing 8-2 creates a new +`Vec` that holds the values `1`, `2`, and `3`. The integer type is `i32` +because that’s the default integer type, as we discussed in the “Data Types” +section of Chapter 3. ``` let v = vec![1, 2, 3]; @@ -109,18 +114,17 @@ Listing 8-4: Showing where the vector and its elements are dropped When the vector gets dropped, all of its contents are also dropped, meaning those integers it holds will be cleaned up. This may seem like a -straightforward point but can get a bit more complicated when you start to -introduce references to the elements of the vector. Let’s tackle that next! +straightforward point but it can get complicated when you start to introduce +references to the elements of the vector. Let’s tackle that next! ### Reading Elements of Vectors -Now that you know how to create, update, and destroy vectors, knowing how to -read their contents is a good next step. There are two ways to reference a -value stored in a vector. In the examples, we’ve annotated the types of the -values that are returned from these functions for extra clarity. +There are two ways to reference a value stored in a vector: via indexing or +using the `get` method. In the following examples, we’ve annotated the types of +the values that are returned from these functions for extra clarity. -Listing 8-5 shows both methods of accessing a value in a vector, either with -indexing syntax or the `get` method. +Listing 8-5 shows both methods of accessing a value in a vector, with indexing +syntax and the `get` method. ``` let v = vec![1, 2, 3, 4, 5]; @@ -138,16 +142,16 @@ Listing 8-5: Using indexing syntax or the `get` method to access an item in a vector Note two details here. First, we use the index value of `2` to get the third -element: vectors are indexed by number, starting at zero. Second, the two ways -to get the third element are by using `&` and `[]`, which gives us a reference, -or by using the `get` method with the index passed as an argument, which gives -us an `Option<&T>`. +element because vectors are indexed by number, starting at zero. Second, we get +the third element by either using `&` and `[]`, which gives us a reference, or +using the `get` method with the index passed as an argument, which gives us an +`Option<&T>`. -Rust has two ways to reference an element so you can choose how the program -behaves when you try to use an index value that the vector doesn’t have an -element for. As an example, let’s see what a program will do if it has a vector -that holds five elements and then tries to access an element at index 100, as -shown in Listing 8-6. +The reason Rust provides these two ways to reference an element is so you can +choose how the program behaves when you try to use an index value outside the +range of existing elements. As an example, let’s see what happens when we have +a vector of five elements and then we try to access an element at index 100 +with each technique, as shown in Listing 8-6. ``` let v = vec![1, 2, 3, 4, 5]; @@ -166,21 +170,23 @@ end of the vector. When the `get` method is passed an index that is outside the vector, it returns `None` without panicking. You would use this method if accessing an element -beyond the range of the vector happens occasionally under normal circumstances. -Your code will then have logic to handle having either `Some(&element)` or -`None`, as discussed in Chapter 6. For example, the index could be coming from -a person entering a number. If they accidentally enter a number that’s too -large and the program gets a `None` value, you could tell the user how many -items are in the current vector and give them another chance to enter a valid -value. That would be more user-friendly than crashing the program due to a typo! +beyond the range of the vector may happen occasionally under normal +circumstances. Your code will then have logic to handle having either +`Some(&element)` or `None`, as discussed in Chapter 6. For example, the index +could be coming from a person entering a number. If they accidentally enter a +number that’s too large and the program gets a `None` value, you could tell the +user how many items are in the current vector and give them another chance to +enter a valid value. That would be more user-friendly than crashing the program +due to a typo! When the program has a valid reference, the borrow checker enforces the ownership and borrowing rules (covered in Chapter 4) to ensure this reference and any other references to the contents of the vector remain valid. Recall the rule that states you can’t have mutable and immutable references in the same -scope. That rule applies in Listing 8-7, where we hold an immutable reference to -the first element in a vector and try to add an element to the end, which won’t -work if we also try to refer to that element later in the function: +scope. That rule applies in Listing 8-7, where we hold an immutable reference +to the first element in a vector and try to add an element to the end. This +program won’t work if we also try to refer to that element later in the +function: ``` let mut v = vec![1, 2, 3, 4, 5]; @@ -211,23 +217,24 @@ Compiling this code will result in this error: ``` The code in Listing 8-7 might look like it should work: why should a reference -to the first element care about what changes at the end of the vector? This -error is due to the way vectors work: adding a new element onto the end of the -vector might require allocating new memory and copying the old elements to the -new space, if there isn’t enough room to put all the elements next to each -other where the vector currently is. In that case, the reference to the first -element would be pointing to deallocated memory. The borrowing rules prevent -programs from ending up in that situation. +to the first element care about changes at the end of the vector? This error is +due to the way vectors work: because vectors put the values next to each other +in memory, adding a new element onto the end of the vector might require +allocating new memory and copying the old elements to the new space, if there +isn’t enough room to put all the elements next to each other where the vector +is currently stored. In that case, the reference to the first element would be +pointing to deallocated memory. The borrowing rules prevent programs from +ending up in that situation. > Note: For more on the implementation details of the `Vec` type, see “The > Rustonomicon” at *https://doc.rust-lang.org/nomicon/vec/vec.html*. -### Iterating Over the Values in a Vector +### Iterating over the Values in a Vector -If we want to access each element in a vector in turn, we can iterate through -all of the elements rather than use indices to access one at a time. Listing -8-8 shows how to use a `for` loop to get immutable references to each element -in a vector of `i32` values and print them. +To access each element in a vector in turn, we would iterate through all of the +elements rather than use indices to access one at a time. Listing 8-8 shows how +to use a `for` loop to get immutable references to each element in a vector of +`i32` values and print them. ``` let v = vec![100, 32, 57]; @@ -253,25 +260,25 @@ for i in &mut v { Listing 8-9: Iterating over mutable references to elements in a vector To change the value that the mutable reference refers to, we have to use the -dereference operator (`*`) to get to the value in `i` before we can use the +`*` dereference operator to get to the value in `i` before we can use the `+=` operator. We’ll talk more about the dereference operator in the “Following the Pointer to the Value with the Dereference Operator” section of Chapter 15. ### Using an Enum to Store Multiple Types -At the beginning of this chapter, we said that vectors can only store values -that are the same type. This can be inconvenient; there are definitely use -cases for needing to store a list of items of different types. Fortunately, the -variants of an enum are defined under the same enum type, so when we need to -store elements of a different type in a vector, we can define and use an enum! +Vectors can only store values that are the same type. This can be inconvenient; +there are definitely use cases for needing to store a list of items of +different types. Fortunately, the variants of an enum are defined under the +same enum type, so when we need one type to represent elements of different +types, we can define and use an enum! For example, say we want to get values from a row in a spreadsheet in which some of the columns in the row contain integers, some floating-point numbers, and some strings. We can define an enum whose variants will hold the different -value types, and then all the enum variants will be considered the same type: -that of the enum. Then we can create a vector that holds that enum and so, -ultimately, holds different types. We’ve demonstrated this in Listing 8-10. +value types, and all the enum variants will be considered the same type: that +of the enum. Then we can create a vector to hold that enum and so, ultimately, +holds different types. We’ve demonstrated this in Listing 8-10. ``` enum SpreadsheetCell { @@ -291,17 +298,16 @@ Listing 8-10: Defining an `enum` to store values of different types in one vector Rust needs to know what types will be in the vector at compile time so it knows -exactly how much memory on the heap will be needed to store each element. A -secondary advantage is that we can be explicit about what types are allowed in -this vector. If Rust allowed a vector to hold any type, there would be a chance -that one or more of the types would cause errors with the operations performed -on the elements of the vector. Using an enum plus a `match` expression means -that Rust will ensure at compile time that every possible case is handled, as -discussed in Chapter 6. - -When you’re writing a program, if you don’t know the exhaustive set of types -the program will get at runtime to store in a vector, the enum technique won’t -work. Instead, you can use a trait object, which we’ll cover in Chapter 17. +exactly how much memory on the heap will be needed to store each element. We +must also be explicit about what types are allowed in this vector. If Rust +allowed a vector to hold any type, there would be a chance that one or more of +the types would cause errors with the operations performed on the elements of +the vector. Using an enum plus a `match` expression means that Rust will ensure +at compile time that every possible case is handled, as discussed in Chapter 6. + +If you don’t know the exhaustive set of types a program will get at runtime to +store in a vector, the enum technique won’t work. Instead, you can use a trait +object, which we’ll cover in Chapter 17. Now that we’ve discussed some of the most common ways to use vectors, be sure to review the API documentation for all the many useful methods defined on @@ -318,8 +324,8 @@ complicated data structure than many programmers give them credit for, and UTF-8. These factors combine in a way that can seem difficult when you’re coming from other programming languages. -It’s useful to discuss strings in the context of collections because strings -are implemented as a collection of bytes, plus some methods to provide useful +We discuss strings in the context of collections because strings are +implemented as a collection of bytes, plus some methods to provide useful functionality when those bytes are interpreted as text. In this section, we’ll talk about the operations on `String` that every collection type has, such as creating, updating, and reading. We’ll also discuss the ways in which `String` @@ -338,8 +344,13 @@ string slices. The `String` type, which is provided by Rust’s standard library rather than coded into the core language, is a growable, mutable, owned, UTF-8 encoded -string type. When Rustaceans refer to “strings” in Rust, they usually mean the -`String` and the string slice `&str` types, not just one of those types. +string type. When Rustaceans refer to “strings” in Rust, they might be +referring to either the `String` or the string slice `&str` types, not just one +of those types. + + Although this section is largely about `String`, both types are used heavily in Rust’s standard library, and both `String` and string slices are UTF-8 encoded. @@ -398,7 +409,8 @@ string literal Because strings are used for so many things, we can use many different generic APIs for strings, providing us with a lot of options. Some of them can seem redundant, but they all have their place! In this case, `String::from` and -`to_string` do the same thing, so which you choose is a matter of style. +`to_string` do the same thing, so which you choose is a matter of style and +readability. Remember that strings are UTF-8 encoded, so we can include any properly encoded data in them, as shown in Listing 8-14. @@ -441,8 +453,8 @@ Listing 8-15: Appending a string slice to a `String` using the `push_str` method After these two lines, `s` will contain `foobar`. The `push_str` method takes a string slice because we don’t necessarily want to take ownership of the -parameter. For example, the code in Listing 8-16 shows that it would be -unfortunate if we weren’t able to use `s2` after appending its contents to `s1`. +parameter. For example, in the code in Listing 8-16, we want to able to use +`s2` after appending its contents to `s1`. ``` let mut s1 = String::from("foo"); @@ -457,8 +469,8 @@ If the `push_str` method took ownership of `s2`, we wouldn’t be able to print its value on the last line. However, this code works as we’d expect! The `push` method takes a single character as a parameter and adds it to the -`String`. Listing 8-17 shows code that adds the letter "l" to a `String` using -the `push` method. +`String`. Listing 8-17 adds the letter "l" to a `String` using the `push` +method. ``` let mut s = String::from("lo"); @@ -467,12 +479,12 @@ s.push('l'); Listing 8-17: Adding one character to a `String` value using `push` -As a result of this code, `s` will contain `lol`. +As a result, `s` will contain `lol`. #### Concatenation with the `+` Operator or the `format!` Macro -Often, you’ll want to combine two existing strings. One way is to use the `+` -operator, as shown in Listing 8-18. +Often, you’ll want to combine two existing strings. One way to do so is to use +the `+` operator, as shown in Listing 8-18. ``` let s1 = String::from("Hello, "); @@ -483,28 +495,27 @@ let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used Listing 8-18: Using the `+` operator to combine two `String` values into a new `String` value -The string `s3` will contain `Hello, world!` as a result of this code. The -reason `s1` is no longer valid after the addition and the reason we used a -reference to `s2` has to do with the signature of the method that gets called -when we use the `+` operator. The `+` operator uses the `add` method, whose -signature looks something like this: +The string `s3` will contain `Hello, world!`. The reason `s1` is no longer +valid after the addition, and the reason we used a reference to `s2`, has to do +with the signature of the method that’s called when we use the `+` operator. +The `+` operator uses the `add` method, whose signature looks something like +this: ``` fn add(self, s: &str) -> String { ``` -This isn’t the exact signature that’s in the standard library: in the standard -library, `add` is defined using generics. Here, we’re looking at the signature -of `add` with concrete types substituted for the generic ones, which is what -happens when we call this method with `String` values. We’ll discuss generics -in Chapter 10. This signature gives us the clues we need to understand the -tricky bits of the `+` operator. +In the standard library, you'll see `add` defined using generics. Here, we’ve +substituted in concrete types for the generic ones, which is what happens when +we call this method with `String` values. We’ll discuss generics in Chapter 10. +This signature gives us the clues we need to understand the tricky bits of the +`+` operator. First, `s2` has an `&`, meaning that we’re adding a *reference* of the second -string to the first string because of the `s` parameter in the `add` function: -we can only add a `&str` to a `String`; we can’t add two `String` values -together. But wait—the type of `&s2` is `&String`, not `&str`, as specified in -the second parameter to `add`. So why does Listing 8-18 compile? +string to the first string. This is because of the `s` parameter in the `add` +function: we can only add a `&str` to a `String`; we can’t add two `String` +values together. But wait—the type of `&s2` is `&String`, not `&str`, as +specified in the second parameter to `add`. So why does Listing 8-18 compile? The reason we’re able to use `&s2` in the call to `add` is that the compiler can *coerce* the `&String` argument into a `&str`. When we call the `add` @@ -515,12 +526,12 @@ after this operation. Second, we can see in the signature that `add` takes ownership of `self`, because `self` does *not* have an `&`. This means `s1` in Listing 8-18 will be -moved into the `add` call and no longer be valid after that. So although `let -s3 = s1 + &s2;` looks like it will copy both strings and create a new one, this -statement actually takes ownership of `s1`, appends a copy of the contents of -`s2`, and then returns ownership of the result. In other words, it looks like -it’s making a lot of copies but isn’t; the implementation is more efficient -than copying. +moved into the `add` call and will no longer be valid after that. So although +`let s3 = s1 + &s2;` looks like it will copy both strings and create a new one, +this statement actually takes ownership of `s1`, appends a copy of the contents +of `s2`, and then returns ownership of the result. In other words, it looks +like it’s making a lot of copies but isn’t; the implementation is more +efficient than copying. If we need to concatenate multiple strings, the behavior of the `+` operator gets unwieldy: @@ -535,7 +546,7 @@ let s = s1 + "-" + &s2 + "-" + &s3; At this point, `s` will be `tic-tac-toe`. With all of the `+` and `"` characters, it’s difficult to see what’s going on. For more complicated string -combining, we can use the `format!` macro: +combining, we can instead use the `format!` macro: ``` let s1 = String::from("tic"); @@ -545,9 +556,9 @@ let s3 = String::from("toe"); let s = format!("{}-{}-{}", s1, s2, s3); ``` -This code also sets `s` to `tic-tac-toe`. The `format!` macro works in the same -way as `println!`, but instead of printing the output to the screen, it returns -a `String` with the contents. The version of the code using `format!` is much +This code also sets `s` to `tic-tac-toe`. The `format!` macro works like +`println!`, but instead of printing the output to the screen, it returns a +`String` with the contents. The version of the code using `format!` is much easier to read, and the code generated by the `format!` macro uses references so that this call doesn’t take ownership of any of its parameters. @@ -591,15 +602,15 @@ let hello = String::from("Hola"); ``` In this case, `len` will be 4, which means the vector storing the string “Hola” -is 4 bytes long. Each of these letters takes 1 byte when encoded in UTF-8. But -what about the following line? (Note that this string begins with the capital -Cyrillic letter Ze, not the Arabic number 3.) +is 4 bytes long. Each of these letters takes 1 byte when encoded in UTF-8. The +following line, however, may surprise you. (Note that this string begins with +the capital Cyrillic letter Ze, not the Arabic number 3.) ``` let hello = String::from("Здравствуйте"); ``` -Asked how long the string is, you might say 12. However, Rust’s answer is 24: +Asked how long the string is, you might say 12. In fact, Rust’s answer is 24: that’s the number of bytes it takes to encode “Здравствуйте” in UTF-8, because each Unicode scalar value in that string takes 2 bytes of storage. Therefore, an index into the string’s bytes will not always correlate to a valid Unicode @@ -610,17 +621,18 @@ let hello = "Здравствуйте"; let answer = &hello[0]; ``` -What should the value of `answer` be? Should it be `З`, the first letter? When -encoded in UTF-8, the first byte of `З` is `208` and the second is `151`, so -`answer` should in fact be `208`, but `208` is not a valid character on its -own. Returning `208` is likely not what a user would want if they asked for the -first letter of this string; however, that’s the only data that Rust has at -byte index 0. Users generally don’t want the byte value returned, even if the -string contains only Latin letters: if `&"hello"[0]` were valid code that -returned the byte value, it would return `104`, not `h`. To avoid returning an -unexpected value and causing bugs that might not be discovered immediately, -Rust doesn’t compile this code at all and prevents misunderstandings early in -the development process. +You already know that `answer` will not be `З`, the first letter. When encoded +in UTF-8, the first byte of `З` is `208` and the second is `151`, so it would +seem that `answer` should in fact be `208`, but `208` is not a valid character +on its own. Returning `208` is likely not what a user would want if they asked +for the first letter of this string; however, that’s the only data that Rust +has at byte index 0. Users generally don’t want the byte value returned, even +if the string contains only Latin letters: if `&"hello"[0]` were valid code +that returned the byte value, it would return `104`, not `h`. + +The answer, then, is that to avoid returning an unexpected value and causing +bugs that might not be discovered immediately, Rust doesn’t compile this code +at all and prevents misunderstandings early in the development process. #### Bytes and Scalar Values and Grapheme Clusters! Oh My! @@ -667,10 +679,10 @@ index to determine how many valid characters there were. Indexing into a string is often a bad idea because it’s not clear what the return type of the string-indexing operation should be: a byte value, a -character, a grapheme cluster, or a string slice. Therefore, Rust asks you to -be more specific if you really need to use indices to create string slices. To -be more specific in your indexing and indicate that you want a string slice, -rather than indexing using `[]` with a single number, you can use `[]` with a +character, a grapheme cluster, or a string slice. If you really need to use +indices to create string slices, therefore, Rust asks you to be more specific. + +Rather than indexing using `[]` with a single number, you can use `[]` with a range to create a string slice containing particular bytes: ``` @@ -683,8 +695,9 @@ Here, `s` will be a `&str` that contains the first 4 bytes of the string. Earlier, we mentioned that each of these characters was 2 bytes, which means `s` will be `Зд`. -What would happen if we used `&hello[0..1]`? The answer: Rust would panic at -runtime in the same way as if an invalid index were accessed in a vector: +If we were to try to slice only part of a character's bytes with something like +`&hello[0..1]`, Rust would panic at runtime in the same way as if an invalid +index were accessed in a vector: ``` thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside 'З' (bytes 0..2) of `Здравствуйте`', src/main.rs:4:14 @@ -695,12 +708,20 @@ can crash your program. ### Methods for Iterating Over Strings -Fortunately, you can access elements in a string in other ways. + + + +The best way to operate on pieces of strings is to be explicit about whether +you want characters or bytes. For individual Unicode scalar values, use the +`chars` method. Calling `chars` on “नमस्ते” separates out and returns six values +of type `char`, and you can iterate over the result to access each element: ``` for c in "नमस्ते".chars() { @@ -719,8 +740,8 @@ This code will print the following: े ``` -The `bytes` method returns each raw byte, which might be appropriate for your -domain: +Alternatively, the `bytes` method returns each raw byte, which might be +appropriate for your domain: ``` for b in "नमस्ते".bytes() { @@ -742,8 +763,8 @@ But be sure to remember that valid Unicode scalar values may be made up of more than 1 byte. Getting grapheme clusters from strings is complex, so this functionality is not -provided by the standard library. Crates are available on crates.io if this is -the functionality you need. +provided by the standard library. Crates are available on *https://crates.io/* +if this is the functionality you need. ### Strings Are Not So Simple @@ -761,7 +782,7 @@ Let’s switch to something a bit less complex: hash maps! ## Storing Keys with Associated Values in Hash Maps The last of our common collections is the *hash map*. The type `HashMap` -stores a mapping of keys of type `K` to values of type `V`. It does this via a +stores a mapping of keys of type `K` to values of type `V` using a *hashing function*, which determines how it places these keys and values into memory. Many programming languages support this kind of data structure, but they often use a different name, such as hash, map, object, hash table, @@ -779,10 +800,10 @@ As always, check the standard library documentation for more information. ### Creating a New Hash Map -You can create an empty hash map with `new` and add elements with `insert`. In -Listing 8-20, we’re keeping track of the scores of two teams whose names are -Blue and Yellow. The Blue team starts with 10 points, and the Yellow team -starts with 50. +One way to create an empty hash map is using `new` and adding elements with +`insert`. In Listing 8-20, we’re keeping track of the scores of two teams whose +names are *Blue* and *Yellow*. The Blue team starts with 10 points, and the +Yellow team starts with 50. ``` use std::collections::HashMap; @@ -806,16 +827,21 @@ keys of type `String` and values of type `i32`. Like vectors, hash maps are homogeneous: all of the keys must have the same type, and all of the values must have the same type. + + + Another way of constructing a hash map is by using iterators and the `collect` method on a vector of tuples, where each tuple consists of a key and its value. We’ll be going into more detail about iterators and their associated methods in -the ”Processing a Series of Items with Iterators” section of Chapter -13. The `collect` method gathers data into a number -of collection types, including `HashMap`. For example, if we had the team names -and initial scores in two separate vectors, we could use the `zip` method to -create an iterator of tuples where “Blue” is paired with 10, and so forth. Then -we could use the `collect` method to turn that iterator of tuples into a hash -map, as shown in Listing 8-21. +the ”Processing a Series of Items with Iterators” section of Chapter 13. The +`collect` method gathers data into a number of collection types, including +`HashMap`. For example, if we had the team names and initial scores in two +separate vectors, we could use the `zip` method to create an iterator of tuples +where “Blue” is paired with 10, and so forth. Then we could use the `collect` +method to turn that iterator of tuples into a hash map, as shown in Listing +8-21. ``` use std::collections::HashMap; @@ -834,8 +860,8 @@ The type annotation `HashMap<_, _>` is needed here because it’s possible to want unless you specify. For the parameters for the key and value types, however, we use underscores, and Rust can infer the types that the hash map contains based on the types of the data in the vectors. In Listing 8-21, the -key type will be `String` and the value type will be `i32`, just as the types -were in Listing 8-20. +key type will be `String` and the value type will be `i32`, just as in Listing +8-20. ### Hash Maps and Ownership @@ -916,14 +942,20 @@ Blue: 10 ### Updating a Hash Map -Although the number of keys and values is growable, each key can only have one -value associated with it at a time. When you want to change the data in a hash -map, you have to decide how to handle the case when a key already has a value -assigned. You could replace the old value with the new value, completely -disregarding the old value. You could keep the old value and ignore the new -value, only adding the new value if the key *doesn’t* already have a value. Or -you could combine the old value and the new value. Let’s look at how to do each -of these! +Although the number of key and value pairs is growable, each key can only have +one value associated with it at a time. + + +When you want to change the data in a hash map, you have to decide how to +handle the case when a key already has a value assigned. You could replace the +old value with the new value, completely disregarding the old value. You could +keep the old value and ignore the new value, only adding the new value if the +key *doesn’t* already have a value. Or you could combine the old value and the +new value. Let’s look at how to do each of these! #### Overwriting a Value @@ -951,6 +983,19 @@ overwritten. #### Only Inserting a Value If the Key Has No Value + + + It’s common to check whether a particular key has a value and, if it doesn’t, insert a value for it. Hash maps have a special API for this called `entry` that takes the key you want to check as a parameter. The return value of the @@ -1025,17 +1070,17 @@ allowed by the borrowing rules. ### Hashing Functions -By default, `HashMap` uses a hashing function called SipHash that can provide +By default, `HashMap` uses a hashing function called *SipHash* that can provide resistance to Denial of Service (DoS) attacks involving hash tables. This is not the fastest hashing algorithm available, but the trade-off for better security that comes with the drop in performance is worth it. If you profile your code and find that the default hash function is too slow for your -purposes, you can switch to another function by specifying a different -*hasher*. A hasher is a type that implements the `BuildHasher` trait. We’ll -talk about traits and how to implement them in Chapter 10. You don’t -necessarily have to implement your own hasher from scratch; crates.io has -libraries shared by other Rust users that provide hashers implementing many -common hashing algorithms. +purposes, you can switch to another function by specifying a different hasher. +A *hasher* is a type that implements the `BuildHasher` trait. We’ll talk about +traits and how to implement them in Chapter 10. You don’t necessarily have to +implement your own hasher from scratch; *https://crates.io/* has libraries +shared by other Rust users that provide hashers implementing many common +hashing algorithms. ## Summary diff --git a/src/doc/book/nostarch/chapter09.md b/src/doc/book/nostarch/chapter09.md index c3c4e057ba..54f0afceaa 100644 --- a/src/doc/book/nostarch/chapter09.md +++ b/src/doc/book/nostarch/chapter09.md @@ -1,21 +1,30 @@ + [TOC] # Error Handling -Rust’s commitment to reliability extends to error handling. Errors are a fact -of life in software, so Rust has a number of features for handling situations -in which something goes wrong. In many cases, Rust requires you to acknowledge -the possibility of an error and take some action before your code will compile. -This requirement makes your program more robust by ensuring that you’ll -discover errors and handle them appropriately before you’ve deployed your code -to production! +Errors are a fact of life in software, so Rust has a number of features for +handling situations in which something goes wrong. In many cases, Rust requires +you to acknowledge the possibility of an error and take some action before your +code will compile. This requirement makes your program more robust by ensuring +that you’ll discover errors and handle them appropriately before you’ve +deployed your code to production! Rust groups errors into two major categories: *recoverable* and *unrecoverable* -errors. For a recoverable error, such as a file not found error, it’s -reasonable to report the problem to the user and retry the operation. +errors. For a recoverable error, such as a *file not found* error, we most +likely just want to report the problem to the user and retry the operation. Unrecoverable errors are always symptoms of bugs, like trying to access a -location beyond the end of an array. +location beyond the end of an array, and so we want to immediately stop the +program. + + Most languages don’t distinguish between these two kinds of errors and handle both in the same way, using mechanisms such as exceptions. Rust doesn’t have @@ -31,16 +40,27 @@ execution. Sometimes, bad things happen in your code, and there’s nothing you can do about it. In these cases, Rust has the `panic!` macro. When the `panic!` macro executes, your program will print a failure message, unwind and clean up the -stack, and then quit. This most commonly occurs when a bug of some kind has -been detected and it’s not clear to the programmer how to handle the error. +stack, and then quit. We’ll commonly invoke a panic when a bug of some kind has +been detected and it’s not clear how to handle the problem at the time we’re +writing our program. + + + > ### Unwinding the Stack or Aborting in Response to a Panic > > By default, when a panic occurs, the program starts *unwinding*, which > means Rust walks back up the stack and cleans up the data from each function -> it encounters. But this walking back and cleanup is a lot of work. The -> alternative is to immediately *abort*, which ends the program without -> cleaning up. Memory that the program was using will then need to be cleaned +> it encounters. However, this walking back and cleanup is a lot of work. Rust, therefore, +> allows you to choose the alternative of immediately *aborting*, which ends the program without +> cleaning up. +> +> +> Memory that the program was using will then need to be cleaned > up by the operating system. If in your project you need to make the resulting > binary as small as possible, you can switch from unwinding to aborting upon a > panic by adding `panic = 'abort'` to the appropriate `[profile]` sections in @@ -48,7 +68,7 @@ been detected and it’s not clear to the programmer how to handle the error. > mode, add this: > > ```toml -> profile.release +> [profile.release] > panic = 'abort' > ``` @@ -80,15 +100,15 @@ be in code that our code calls, and the filename and line number reported by the error message will be someone else’s code where the `panic!` macro is called, not the line of our code that eventually led to the `panic!` call. We can use the backtrace of the functions the `panic!` call came from to figure -out the part of our code that is causing the problem. We’ll discuss what a -backtrace is in more detail next. +out the part of our code that is causing the problem. We’ll discuss backtraces +in more detail next. ### Using a `panic!` Backtrace Let’s look at another example to see what it’s like when a `panic!` call comes from a library because of a bug in our code instead of from our code calling the macro directly. Listing 9-1 has some code that attempts to access an -element by index in a vector. +index in a vector beyond the range of valid indexes. Filename: src/main.rs @@ -104,10 +124,10 @@ Listing 9-1: Attempting to access an element beyond the end of a vector, which will cause a call to `panic!` Here, we’re attempting to access the 100th element of our vector (which is at -index 99 because indexing starts at zero), but it has only 3 elements. In this -situation, Rust will panic. Using `[]` is supposed to return an element, but if -you pass an invalid index, there’s no element that Rust could return here that -would be correct. +index 99 because indexing starts at zero), but the vector has only 3 elements. +In this situation, Rust will panic. Using `[]` is supposed to return an +element, but if you pass an invalid index, there’s no element that Rust could +return here that would be correct. In C, attempting to read beyond the end of a data structure is undefined behavior. You might get whatever is at the location in memory that would @@ -133,8 +153,8 @@ error. A *backtrace* is a list of all the functions that have been called to get to this point. Backtraces in Rust work as they do in other languages: the key to reading the backtrace is to start from the top and read until you see files you wrote. That’s the spot where the problem originated. The lines above -the lines mentioning your files are code that your code called; the lines below -are code that called your code. These lines might include core Rust code, +that spot are code that your code has called; the lines below are code that +called your code. These before-and-after lines might include core Rust code, standard library code, or crates that you’re using. Let’s try getting a backtrace by setting the `RUST_BACKTRACE` environment variable to any value except 0. Listing 9-2 shows output similar to what you’ll see. @@ -171,15 +191,14 @@ information, debug symbols must be enabled. Debug symbols are enabled by default when using `cargo build` or `cargo run` without the `--release` flag, as we have here. -In the output in Listing 9-2, line 6 of the backtrace points to the line in -our project that’s causing the problem: line 4 of *src/main.rs*. If we don’t -want our program to panic, the location pointed to by the first line mentioning -a file we wrote is where we should start investigating. In Listing 9-1, where -we deliberately wrote code that would panic in order to demonstrate how to use -backtraces, the way to fix the panic is to not request an element at index 99 -from a vector that only contains 3 items. When your code panics in the future, -you’ll need to figure out what action the code is taking with what values to -cause the panic and what the code should do instead. +In the output in Listing 9-2, line 6 of the backtrace points to the line in our +project that’s causing the problem: line 4 of *src/main.rs*. If we don’t want +our program to panic, we should start our investigation at the location pointed +to by the first line mentioning a file we wrote. In Listing 9-1, where we +deliberately wrote code that would panic, the way to fix the panic is to not +request an element beyond the range of the vector indexes. When your code +panics in the future, you’ll need to figure out what action the code is taking +with what values to cause the panic and what the code should do instead. We’ll come back to `panic!` and when we should and should not use `panic!` to handle error conditions in the “To `panic!` or Not to `panic!`” section later @@ -210,9 +229,9 @@ detail in Chapter 10. What you need to know right now is that `T` represents the type of the value that will be returned in a success case within the `Ok` variant, and `E` represents the type of the error that will be returned in a failure case within the `Err` variant. Because `Result` has these generic type -parameters, we can use the `Result` type and the functions that the standard -library has defined on it in many different situations where the successful -value and error value we want to return may differ. +parameters, we can use the `Result` type and the functions defined on it in +many different situations where the successful value and error value we want to +return may differ. Let’s call a function that returns a `Result` value because the function could fail. In Listing 9-3 we try to open a file. @@ -301,10 +320,9 @@ Note that, like the `Option` enum, the `Result` enum and its variants have been brought into scope by the prelude, so we don’t need to specify `Result::` before the `Ok` and `Err` variants in the `match` arms. -Here we tell Rust that when the result is `Ok`, return the inner `file` value -out of the `Ok` variant, and we then assign that file handle value to the -variable `f`. After the `match`, we can use the file handle for reading or -writing. +When the result is `Ok`, this code will return the inner `file` value out of +the `Ok` variant, and we then assign that file handle value to the variable +`f`. After the `match`, we can use the file handle for reading or writing. The other arm of the `match` handles the case where we get an `Err` value from `File::open`. In this example, we’ve chosen to call the `panic!` macro. If @@ -319,13 +337,13 @@ As usual, this output tells us exactly what has gone wrong. ### Matching on Different Errors -The code in Listing 9-4 will `panic!` no matter why `File::open` failed. What -we want to do instead is take different actions for different failure reasons: -if `File::open` failed because the file doesn’t exist, we want to create the -file and return the handle to the new file. If `File::open` failed for any -other reason—for example, because we didn’t have permission to open the file—we -still want the code to `panic!` in the same way as it did in Listing 9-4. Look -at Listing 9-5, which adds an inner `match` expression. +The code in Listing 9-4 will `panic!` no matter why `File::open` failed. +However, we want to take different actions for different failure reasons: if +`File::open` failed because the file doesn’t exist, we want to create the file +and return the handle to the new file. If `File::open` failed for any other +reason—for example, because we didn’t have permission to open the file—we still +want the code to `panic!` in the same way as it did in Listing 9-4. For this we +add an inner `match` expression, shown in Listing 8-5. Filename: src/main.rs @@ -370,41 +388,53 @@ file can’t be created, a different error message is printed. The second arm of the outer `match` stays the same, so the program panics on any error besides the missing file error. -That’s a lot of `match`! The `match` expression is very useful but also very -much a primitive. In Chapter 13, you’ll learn about closures; the `Result` type has many methods that accept a closure and are implemented using -`match` expressions. Using those methods will make your code more concise. A -more seasoned Rustacean might write this code instead of Listing 9-5: - -``` -use std::fs::File; -use std::io::ErrorKind; - -fn main() { - let f = File::open("hello.txt").unwrap_or_else(|error| { - if error.kind() == ErrorKind::NotFound { - File::create("hello.txt").unwrap_or_else(|error| { - panic!("Problem creating the file: {:?}", error); - }) - } else { - panic!("Problem opening the file: {:?}", error); - } - }); -} -``` + + -Although this code has the same behavior as Listing 9-5, it doesn’t contain any -`match` expressions and is cleaner to read. Come back to this example after -you’ve read Chapter 13, and look up the `unwrap_or_else` method in the standard -library documentation. Many more of these methods can clean up huge nested -`match` expressions when you’re dealing with errors. +> ### Alternatives to Using `match` with `Result` +> +> That’s a lot of `match`! The `match` expression is very useful but also very +> much a primitive. In Chapter 13, you’ll learn about closures, which are used +> with many of the methods defined on `Result`. These methods can be more +> concise than using `match` when handling `Result` values in your code. +> +> +> +> +> +> +> For example, here’s another way to write the same logic as shown in Listing +> 9-5 but using closures and the `unwrap_or_else` method: +> +> ``` +> use std::fs::File; +> use std::io::ErrorKind; +> +> fn main() { +> let f = File::open("hello.txt").unwrap_or_else(|error| { +> if error.kind() == ErrorKind::NotFound { +> File::create("hello.txt").unwrap_or_else(|error| { +> panic!("Problem creating the file: {:?}", error); +> }) +> } else { +> panic!("Problem opening the file: {:?}", error); +> } +> }); +> } +> ``` +> +> Although this code has the same behavior as Listing 9-5, it doesn’t contain +> any `match` expressions and is cleaner to read. Come back to this example +> after you’ve read Chapter 13, and look up the `unwrap_or_else` method in the +> standard library documentation. Many more of these methods can clean up huge +> nested `match` expressions when you’re dealing with errors. ### Shortcuts for Panic on Error: `unwrap` and `expect` Using `match` works well enough, but it can be a bit verbose and doesn’t always communicate intent well. The `Result` type has many helper methods -defined on it to do various tasks. One of those methods, called `unwrap`, is a -shortcut method that is implemented just like the `match` expression we wrote in +defined on it to do various, more specific tasks. The `unwrap` method is a +shortcut method implemented just like the `match` expression we wrote in Listing 9-4. If the `Result` value is the `Ok` variant, `unwrap` will return the value inside the `Ok`. If the `Result` is the `Err` variant, `unwrap` will call the `panic!` macro for us. Here is an example of `unwrap` in action: @@ -428,10 +458,10 @@ repr: Os { code: 2, message: "No such file or directory" } }', src/libcore/result.rs:906:4 ``` -Another method, `expect`, which is similar to `unwrap`, lets us also choose the -`panic!` error message. Using `expect` instead of `unwrap` and providing good -error messages can convey your intent and make tracking down the source of a -panic easier. The syntax of `expect` looks like this: +Similarly, the `expect` method lets us also choose the `panic!` error message. +Using `expect` instead of `unwrap` and providing good error messages can convey +your intent and make tracking down the source of a panic easier. The syntax of +`expect` looks like this: Filename: src/main.rs @@ -461,16 +491,16 @@ calls that panic print the same message. ### Propagating Errors -When you’re writing a function whose implementation calls something that might -fail, instead of handling the error within this function, you can return the -error to the calling code so that it can decide what to do. This is known as -*propagating* the error and gives more control to the calling code, where there -might be more information or logic that dictates how the error should be -handled than what you have available in the context of your code. +When a function’s implementation calls something that might fail, instead of +handling the error within the function itself, you can return the error to the +calling code so that it can decide what to do. This is known as *propagating* +the error and gives more control to the calling code, where there might be more +information or logic that dictates how the error should be handled than what +you have available in the context of your code. For example, Listing 9-6 shows a function that reads a username from a file. If the file doesn’t exist or can’t be read, this function will return those errors -to the code that called this function. +to the code that called the function. Filename: src/main.rs @@ -499,20 +529,19 @@ Listing 9-6: A function that returns errors to the calling code using `match` This function can be written in a much shorter way, but we’re going to start by doing a lot of it manually in order to explore error handling; at the end, -we’ll show the shorter way. Let’s look at the return type of the function first: -`Result`. This means the function is returning a value of -the type `Result` where the generic parameter `T` has been filled in -with the concrete type `String` and the generic type `E` has been filled in -with the concrete type `io::Error`. If this function succeeds without any -problems, the code that calls this function will receive an `Ok` value that +we’ll show the shorter way. Let’s look at the return type of the function +first: `Result`. This means the function is returning a +value of the type `Result` where the generic parameter `T` has been +filled in with the concrete type `String`, and the generic type `E` has been +filled in with the concrete type `io::Error`. If this function succeeds without +any problems, the code that calls this function will receive an `Ok` value that holds a `String`—the username that this function read from the file. If this -function encounters any problems, the code that calls this function will -receive an `Err` value that holds an instance of `io::Error` that contains -more information about what the problems were. We chose `io::Error` as the -return type of this function because that happens to be the type of the error -value returned from both of the operations we’re calling in this function’s -body that might fail: the `File::open` function and the `read_to_string` -method. +function encounters any problems, the calling code will receive an `Err` value +that holds an instance of `io::Error` that contains more information about what +the problems were. We chose `io::Error` as the return type of this function +because that happens to be the type of the error value returned from both of +the operations we’re calling in this function’s body that might fail: the +`File::open` function and the `read_to_string` method. The body of the function starts by calling the `File::open` function. Then we handle the `Result` value with a `match` similar to the `match` in Listing 9-4. @@ -535,9 +564,9 @@ that handled the return value of `File::open`. However, we don’t need to explicitly say `return`, because this is the last expression in the function. The code that calls this code will then handle getting either an `Ok` value -that contains a username or an `Err` value that contains an `io::Error`. We -don’t know what the calling code will do with those values. If the calling code -gets an `Err` value, it could call `panic!` and crash the program, use a +that contains a username or an `Err` value that contains an `io::Error`. It’s +up to the calling code to decide what to do with those values. If the calling +code gets an `Err` value, it could call `panic!` and crash the program, use a default username, or look up the username from somewhere other than a file, for example. We don’t have enough information on what the calling code is actually trying to do, so we propagate all the success or error information upward for @@ -549,7 +578,7 @@ question mark operator `?` to make this easier. #### A Shortcut for Propagating Errors: the `?` Operator Listing 9-7 shows an implementation of `read_username_from_file` that has the -same functionality as it had in Listing 9-6, but this implementation uses the +same functionality as in Listing 9-6, but this implementation uses the `?` operator. Filename: src/main.rs @@ -627,8 +656,7 @@ username in `s` when both `File::open` and `read_to_string` succeed rather than returning errors. The functionality is again the same as in Listing 9-6 and Listing 9-7; this is just a different, more ergonomic way to write it. -Speaking of different ways to write this function, Listing 9-9 shows that -there’s a way to make this even shorter. +Listing 9-9 shows a way to make this even shorter using `fs::read_to_string`. Filename: src/main.rs @@ -644,25 +672,26 @@ fn read_username_from_file() -> Result { Listing 9-9: Using `fs::read_to_string` instead of opening and then reading the file -Reading a file into a string is a fairly common operation, so Rust provides the -convenient `fs::read_to_string` function that opens the file, creates a new -`String`, reads the contents of the file, puts the contents into that `String`, -and returns it. Of course, using `fs::read_to_string` doesn’t give us the -opportunity to explain all the error handling, so we did it the longer way -first. +Reading a file into a string is a fairly common operation, so the standard +library provides the convenient `fs::read_to_string` function that opens the +file, creates a new `String`, reads the contents of the file, puts the contents +into that `String`, and returns it. Of course, using `fs::read_to_string` +doesn’t give us the opportunity to explain all the error handling, so we did it +the longer way first. #### Where The `?` Operator Can Be Used -The `?` operator can only be used in functions that have a return type -compatible with the value the `?` is used on. This is because the `?` operator -is defined to perform an early return of a value out of the function, in the -same manner as the `match` expression we defined in Listing 9-6 did. In Listing -9-6, the `match` was using a `Result` value, and the early return arm returned -an `Err(e)` value. The return type of the function has to be a `Result` to be -compatible with this `return`. +The `?` operator can only be used in functions whose return type is compatible +with the value the `?` is used on. This is because the `?` operator is defined +to perform an early return of a value out of the function, in the same manner +as the `match` expression we defined in Listing 9-6. In Listing 9-6, the +`match` was using a `Result` value, and the early return arm returned an +`Err(e)` value. The return type of the function has to be a `Result` so that +it’s compatible with this `return`. In Listing 9-10, let’s look at the error we’ll get if we use the `?` operator -in a `main` function with a return type of `()`: +in a `main` function with a return type incompatible with the type of the value +we use `?` on: ``` use std::fs::File; @@ -694,11 +723,15 @@ error[E0277]: the `?` operator can only be used in a function that returns `Resu This error points out that we’re only allowed to use the `?` operator in a function that returns `Result`, `Option`, or another type that implements -`FromResidual`. To fix this error, you have two choices. One technique is to -change the return type of your function to be `Result` if you have no -restrictions preventing that. The other technique is to use a `match` or one of -the `Result` methods to handle the `Result` in whatever way is -appropriate. +`FromResidual`. + + +To fix the error, you have two choices. One choice is to change the return type +of your function to be compatible with the value you’re using the `?` operator +on as long as you have no restrictions preventing that. The other technique is +to use a `match` or one of the `Result` methods to handle the `Result` in whatever way is appropriate. The error message also mentioned that `?` can be used with `Option` values as well. As with using `?` on `Result`, you can only use `?` on `Option` in a @@ -718,47 +751,40 @@ fn last_char_of_first_line(text: &str) -> Option { Listing 9-11: Using the `?` operator on an `Option` value -This function returns `Option` because it might find a character at this -position, or there might be no character there. This code takes the `text` -string slice argument and calls the `lines` method on it, which returns an -iterator over the lines in the string. Because this function wants to examine -the first line, it calls `next` on the iterator to get the first value from the -iterator. If `text` is the empty string, this call to `next` will return -`None`, and here we can use `?` to stop and return `None` from -`last_char_of_first_line` if that is the case. If `text` is not the empty -string, `next` will return a `Some` value containing a string slice of the -first line in `text`. +This function returns `Option` because it’s possible that there is a +character there, but it’s also possible that there isn’t. This code takes the +`text` string slice argument and calls the `lines` method on it, which returns +an iterator over the lines in the string. Because this function wants to +examine the first line, it calls `next` on the iterator to get the first value +from the iterator. If `text` is the empty string, this call to `next` will +return `None`, in which case we use `?` to stop and return `None` from +`last_char_of_first_line`. If `text` is not the empty string, `next` will +return a `Some` value containing a string slice of the first line in `text`. The `?` extracts the string slice, and we can call `chars` on that string slice -to get an iterator of the characters in this string slice. We’re interested in -the last character in this first line, so we call `last` to return the last -item in the iterator over the characters. This is an `Option` because the first -line might be the empty string, if `text` starts with a blank line but has -characters on other lines, as in `"\nhi"`. However, if there is a last -character on the first line, it will be returned in the `Some` variant. The `?` -operator in the middle gives us a concise way to express this logic, and this -function can be implemented in one line. If we couldn’t use the `?` operator on -`Option`, we’d have to implement this logic using more method calls or a -`match` expression. +to get an iterator of its characters. We’re interested in the last character in +this first line, so we call `last` to return the last item in the iterator. +This is an `Option` because it’s possible that the first line is the empty +string, for example if `text` starts with a blank line but has characters on +other lines, as in `"\nhi"`. However, if there is a last character on the first +line, it will be returned in the `Some` variant. The `?` operator in the middle +gives us a concise way to express this logic, allowing us to implement the +function in one line. If we couldn’t use the `?` operator on `Option`, we’d +have to implement this logic using more method calls or a `match` expression. Note that you can use the `?` operator on a `Result` in a function that returns `Result`, and you can use the `?` operator on an `Option` in a function that returns `Option`, but you can’t mix and match. The `?` operator won’t automatically convert a `Result` to an `Option` or vice versa; in those cases, -there are methods like the `ok` method on `Result` or the `ok_or` method on -`Option` that will do the conversion explicitly. +you can use methods like the `ok` method on `Result` or the `ok_or` method on +`Option` to do the conversion explicitly. So far, all the `main` functions we’ve used return `()`. The `main` function is special because it’s the entry and exit point of executable programs, and there are restrictions on what its return type can be for the programs to behave as -expected. Executables written in C return integers when they exit, and Rust -executables follow this convention as well: programs that exit successfully -return the integer `0`, and programs that error return some integer other than -`0`. When `main` returns `()`, Rust executables will return `0` if `main` -returns and a nonzero value if the program panics before reaching the end of -`main`. - -Another return type `main` can have is `Result<(), E>`. Listing 9-12 has the +expected. + +Luckily, `main` can also return a `Result<(), E>`. Listing 9-12 has the code from Listing 9-10 but we’ve changed the return type of `main` to be `Result<(), Box>` and added a return value `Ok(())` to the end. This code will now compile: @@ -777,16 +803,24 @@ fn main() -> Result<(), Box> { Listing 9-12: Changing `main` to return `Result<(), E>` allows the use of the `?` operator on `Result` values -The `Box` type is called a trait object, which we’ll talk about in -the “Using Trait Objects that Allow for Values of Different Types” section in +The `Box` type is a *trait object*, which we’ll talk about in the +“Using Trait Objects that Allow for Values of Different Types” section in Chapter 17. For now, you can read `Box` to mean “any kind of error.” -Using `?` on a `Result` value in a `main` function with this return type is -allowed, because now an `Err` value can be returned early. When a `main` -function returns a `Result<(), E>`, the executable will exit with a value of -`0` if `main` returns `Ok(())` and will exit with a nonzero value if `main` -returns an `Err` value. +Using `?` on a `Result` value in a `main` function with the error type +`Box` is allowed, because it allows any `Err` value to be returned +early. + +When a `main` function returns a `Result<(), E>`, the executable will +exit with a value of `0` if `main` returns `Ok(())` and will exit with a +nonzero value if `main` returns an `Err` value. Executables written in C return +integers when they exit: programs that exit successfully return the integer +`0`, and programs that error return some integer other than `0`. Rust also +returns integers from executables to be compatible with this convention. + + + -The types that `main` may return are those that implement the +The `main` function may return any types that implement the `std::process::Termination` trait. As of this writing, the `Termination` trait is an unstable feature only available in Nightly Rust, so you can’t yet implement it for your own types in Stable Rust, but you might be able to @@ -801,26 +835,24 @@ cases. So how do you decide when you should call `panic!` and when you should return `Result`? When code panics, there’s no way to recover. You could call `panic!` for any error situation, whether there’s a possible way to recover or not, but -then you’re making the decision on behalf of the code calling your code that a -situation is unrecoverable. When you choose to return a `Result` value, you -give the calling code options rather than making the decision for it. The -calling code could choose to attempt to recover in a way that’s appropriate for -its situation, or it could decide that an `Err` value in this case is -unrecoverable, so it can call `panic!` and turn your recoverable error into an -unrecoverable one. Therefore, returning `Result` is a good default choice when -you’re defining a function that might fail. - -In rare situations, it’s more appropriate to write code that panics instead of -returning a `Result`. Let’s explore why it’s appropriate to panic in examples, -prototype code, and tests. Then we’ll discuss situations in which the compiler -can’t tell that failure is impossible, but you as a human can. The chapter will -conclude with some general guidelines on how to decide whether to panic in -library code. +then you’re making the decision that a situation is unrecoverable on behalf of +the calling code. When you choose to return a `Result` value, you give the +calling code options. The calling code could choose to attempt to recover in a +way that’s appropriate for its situation, or it could decide that an `Err` +value in this case is unrecoverable, so it can call `panic!` and turn your +recoverable error into an unrecoverable one. Therefore, returning `Result` is a +good default choice when you’re defining a function that might fail. + +In situations such as examples, prototype code, and tests, it’s more +appropriate to write code that panics instead of returning a `Result`. Let’s +explore why, then discuss situations in which the compiler can’t tell that +failure is impossible, but you as a human can. The chapter will conclude with +some general guidelines on how to decide whether to panic in library code. ### Examples, Prototype Code, and Tests -When you’re writing an example to illustrate some concept, having robust -error-handling code in the example as well can make the example less clear. In +When you’re writing an example to illustrate some concept, also including robust +error-handling code can make the example less clear. In examples, it’s understood that a call to a method like `unwrap` that could panic is meant as a placeholder for the way you’d want your application to handle errors, which can differ based on what the rest of your code is doing. @@ -907,9 +939,9 @@ documentation for the function. However, having lots of error checks in all of your functions would be verbose and annoying. Fortunately, you can use Rust’s type system (and thus the type -checking the compiler does) to do many of the checks for you. If your function -has a particular type as a parameter, you can proceed with your code’s logic -knowing that the compiler has already ensured you have a valid value. For +checking done by the compiler) to do many of the checks for you. If your +function has a particular type as a parameter, you can proceed with your code’s +logic knowing that the compiler has already ensured you have a valid value. For example, if you have a type rather than an `Option`, your program expects to have *something* rather than *nothing*. Your code then doesn’t have to handle two cases for the `Some` and `None` variants: it will only have one case for diff --git a/src/doc/book/nostarch/chapter10.md b/src/doc/book/nostarch/chapter10.md index b259636914..73ce7c6cb6 100644 --- a/src/doc/book/nostarch/chapter10.md +++ b/src/doc/book/nostarch/chapter10.md @@ -1,3 +1,8 @@ + [TOC] diff --git a/src/doc/book/nostarch/chapter11.md b/src/doc/book/nostarch/chapter11.md index 78a88cc827..c1f7dfa50e 100644 --- a/src/doc/book/nostarch/chapter11.md +++ b/src/doc/book/nostarch/chapter11.md @@ -1,3 +1,8 @@ + [TOC] diff --git a/src/doc/book/nostarch/chapter12.md b/src/doc/book/nostarch/chapter12.md index 49d58039d5..e8e3c400f7 100644 --- a/src/doc/book/nostarch/chapter12.md +++ b/src/doc/book/nostarch/chapter12.md @@ -1,3 +1,8 @@ + [TOC] diff --git a/src/doc/book/nostarch/chapter14.md b/src/doc/book/nostarch/chapter14.md new file mode 100644 index 0000000000..ea57d31613 --- /dev/null +++ b/src/doc/book/nostarch/chapter14.md @@ -0,0 +1,1006 @@ + + +[TOC] + +# More About Cargo and Crates.io + +So far we’ve used only the most basic features of Cargo to build, run, and test +our code, but it can do a lot more. In this chapter, we’ll discuss some of its +other, more advanced features to show you how to do the following: + +* Customize your build through release profiles +* Publish libraries on *https://crates.io/* +* Organize large projects with workspaces +* Install binaries from *https://crates.io/* +* Extend Cargo using custom commands + +Cargo can do even more than what we cover in this chapter, so for a full +explanation of all its features, see its documentation at +*https://doc.rust-lang.org/cargo/*. + +## Customizing Builds with Release Profiles + +In Rust, *release profiles* are predefined and customizable profiles with +different configurations that allow a programmer to have more control over +various options for compiling code. Each profile is configured independently of +the others. + +Cargo has two main profiles: the `dev` profile Cargo uses when you run `cargo +build` and the `release` profile Cargo uses when you run `cargo build +--release`. The `dev` profile is defined with good defaults for development, +and the `release` profile has good defaults for release builds. + +These profile names might be familiar from the output of your builds: + +``` +$ cargo build + Finished dev [unoptimized + debuginfo] target(s) in 0.0s +$ cargo build --release + Finished release [optimized] target(s) in 0.0s +``` + +The `dev` and `release` shown in this build output indicate that the compiler +is using different profiles. + +Cargo has default settings for each of the profiles that apply when there +aren’t any `[profile.*]` sections in the project’s *Cargo.toml* file. By adding +`[profile.*]` sections for any profile you want to customize, you can override +any subset of the default settings. For example, here are the default values +for the `opt-level` setting for the `dev` and `release` profiles: + +Filename: Cargo.toml + +``` +[profile.dev] +opt-level = 0 + +[profile.release] +opt-level = 3 +``` + +The `opt-level` setting controls the number of optimizations Rust will apply to +your code, with a range of 0 to 3. Applying more optimizations extends +compiling time, so if you’re in development and compiling your code often, +you’ll want faster compiling even if the resulting code runs slower. That is +the reason the default `opt-level` for `dev` is `0`. When you’re ready to +release your code, it’s best to spend more time compiling. You’ll only compile +in release mode once, but you’ll run the compiled program many times, so +release mode trades longer compile time for code that runs faster. That is why +the default `opt-level` for the `release` profile is `3`. + +You can override any default setting by adding a different value for it in +*Cargo.toml*. For example, if we want to use optimization level 1 in the +development profile, we can add these two lines to our project’s *Cargo.toml* +file: + +Filename: Cargo.toml + +``` +[profile.dev] +opt-level = 1 +``` + +This code overrides the default setting of `0`. Now when we run `cargo build`, +Cargo will use the defaults for the `dev` profile plus our customization to +`opt-level`. Because we set `opt-level` to `1`, Cargo will apply more +optimizations than the default, but not as many as in a release build. + +For the full list of configuration options and defaults for each profile, see +Cargo’s documentation at *https://doc.rust-lang.org/cargo/*. + +## Publishing a Crate to Crates.io + +We’ve used packages from *https://crates.io/* as dependencies of our project, +but you can also share your code with other people by publishing your own +packages. The crate registry at *https://crates.io* distributes the source code +of your packages, so it primarily hosts code that is open source. + +Rust and Cargo have features that help make your published package easier for +people to use and to find in the first place. We’ll talk about some of these +features next and then explain how to publish a package. + +### Making Useful Documentation Comments + +Accurately documenting your packages will help other users know how and when to +use them, so it’s worth investing the time to write documentation. In Chapter +3, we discussed how to comment Rust code using two slashes, `//`. Rust also has +a particular kind of comment for documentation, known conveniently as a +*documentation comment*, that will generate HTML documentation. The HTML +displays the contents of documentation comments for public API items intended +for programmers interested in knowing how to *use* your crate as opposed to how +your crate is *implemented*. + +Documentation comments use three slashes, `///`, instead of two and support +Markdown notation for formatting the text. Place documentation comments just +before the item they’re documenting. Listing 14-1 shows documentation comments +for an `add_one` function in a crate named `my_crate`. + +Filename: src/lib.rs + +``` +/// Adds one to the number given. +/// +/// # Examples +/// +/// ``` +/// let arg = 5; +/// let answer = my_crate::add_one(arg); +/// +/// assert_eq!(6, answer); +/// ``` +pub fn add_one(x: i32) -> i32 { + x + 1 +} +``` + +Listing 14-1: A documentation comment for a function + +Here, we give a description of what the `add_one` function does, start a +section with the heading `Examples`, and then provide code that demonstrates +how to use the `add_one` function. We can generate the HTML documentation from +this documentation comment by running `cargo doc`. This command runs the +`rustdoc` tool distributed with Rust and puts the generated HTML documentation +in the *target/doc* directory. + +For convenience, running `cargo doc --open` will build the HTML for your +current crate’s documentation (as well as the documentation for all of your +crate’s dependencies) and open the result in a web browser. Navigate to the +`add_one` function and you’ll see how the text in the documentation comments is +rendered, as shown in Figure 14-1: + +Rendered HTML documentation for the `add_one` function of `my_crate` + +Figure 14-1: HTML documentation for the `add_one` function + +#### Commonly Used Sections + +We used the `# Examples` Markdown heading in Listing 14-1 to create a section +in the HTML with the title “Examples.” Here are some other sections that crate +authors commonly use in their documentation: + +* **Panics**: The scenarios in which the function being documented could + panic. Callers of the function who don’t want their programs to panic should + make sure they don’t call the function in these situations. +* **Errors**: If the function returns a `Result`, describing the kinds of + errors that might occur and what conditions might cause those errors to be + returned can be helpful to callers so they can write code to handle the + different kinds of errors in different ways. +* **Safety**: If the function is `unsafe` to call (we discuss unsafety in + Chapter 19), there should be a section explaining why the function is unsafe + and covering the invariants that the function expects callers to uphold. + +Most documentation comments don’t need all of these sections, but this is a +good checklist to remind you of the aspects of your code that people calling +your code will be interested in knowing about. + +#### Documentation Comments as Tests + +Adding example code blocks in your documentation comments can help demonstrate +how to use your library, and doing so has an additional bonus: running `cargo +test` will run the code examples in your documentation as tests! Nothing is +better than documentation with examples. But nothing is worse than examples +that don’t work because the code has changed since the documentation was +written. If we run `cargo test` with the documentation for the `add_one` +function from Listing 14-1, we will see a section in the test results like this: + +``` + Doc-tests my_crate + +running 1 test +test src/lib.rs - add_one (line 5) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s +``` + +Now if we change either the function or the example so the `assert_eq!` in the +example panics and run `cargo test` again, we’ll see that the doc tests catch +that the example and the code are out of sync with each other! + +#### Commenting Contained Items + +Another style of doc comment, `//!`, adds documentation to the item that +contains the comments rather than adding documentation to the items following +the comments. We typically use these doc comments inside the crate root file +(*src/lib.rs* by convention) or inside a module to document the crate or the +module as a whole. + +For example, if we want to add documentation that describes the purpose of the +`my_crate` crate that contains the `add_one` function, we can add documentation +comments that start with `//!` to the beginning of the *src/lib.rs* file, as +shown in Listing 14-2: + +Filename: src/lib.rs + +``` +//! # My Crate +//! +//! `my_crate` is a collection of utilities to make performing certain +//! calculations more convenient. + +/// Adds one to the number given. +// --snip-- +``` + +Listing 14-2: Documentation for the `my_crate` crate as a whole + +Notice there isn’t any code after the last line that begins with `//!`. Because +we started the comments with `//!` instead of `///`, we’re documenting the item +that contains this comment rather than an item that follows this comment. In +this case, the item that contains this comment is the *src/lib.rs* file, which +is the crate root. These comments describe the entire crate. + +When we run `cargo doc --open`, these comments will display on the front +page of the documentation for `my_crate` above the list of public items in the +crate, as shown in Figure 14-2: + +Rendered HTML documentation with a comment for the crate as a whole + +Figure 14-2: Rendered documentation for `my_crate`, including the comment +describing the crate as a whole + +Documentation comments within items are useful for describing crates and +modules especially. Use them to explain the overall purpose of the container to +help your users understand the crate’s organization. + +### Exporting a Convenient Public API with `pub use` + +In Chapter 7, we covered how to organize our code into modules using the `mod` +keyword, how to make items public using the `pub` keyword, and how to bring +items into a scope with the `use` keyword. However, the structure that makes +sense to you while you’re developing a crate might not be very convenient for +your users. You might want to organize your structs in a hierarchy containing +multiple levels, but then people who want to use a type you’ve defined deep in +the hierarchy might have trouble finding out that type exists. They might also +be annoyed at having to enter `use` +`my_crate::some_module::another_module::UsefulType;` rather than `use` +`my_crate::UsefulType;`. + +The structure of your public API is a major consideration when publishing a +crate. People who use your crate are less familiar with the structure than you +are and might have difficulty finding the pieces they want to use if your crate +has a large module hierarchy. + +The good news is that if the structure *isn’t* convenient for others to use +from another library, you don’t have to rearrange your internal organization: +instead, you can re-export items to make a public structure that’s different +from your private structure by using `pub use`. Re-exporting takes a public +item in one location and makes it public in another location, as if it were +defined in the other location instead. + +For example, say we made a library named `art` for modeling artistic concepts. +Within this library are two modules: a `kinds` module containing two enums +named `PrimaryColor` and `SecondaryColor` and a `utils` module containing a +function named `mix`, as shown in Listing 14-3: + +Filename: src/lib.rs + +``` +//! # Art +//! +//! A library for modeling artistic concepts. + +pub mod kinds { + /// The primary colors according to the RYB color model. + pub enum PrimaryColor { + Red, + Yellow, + Blue, + } + + /// The secondary colors according to the RYB color model. + pub enum SecondaryColor { + Orange, + Green, + Purple, + } +} + +pub mod utils { + use crate::kinds::*; + + /// Combines two primary colors in equal amounts to create + /// a secondary color. + pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor { + // --snip-- + } +} +``` + +Listing 14-3: An `art` library with items organized into `kinds` and `utils` +modules + +Figure 14-3 shows what the front page of the documentation for this crate +generated by `cargo doc` would look like: + +Rendered documentation for the `art` crate that lists the `kinds` and `utils` modules + +Figure 14-3: Front page of the documentation for `art` that lists the `kinds` +and `utils` modules + +Note that the `PrimaryColor` and `SecondaryColor` types aren’t listed on the +front page, nor is the `mix` function. We have to click `kinds` and `utils` to +see them. + +Another crate that depends on this library would need `use` statements that +bring the items from `art` into scope, specifying the module structure that’s +currently defined. Listing 14-4 shows an example of a crate that uses the +`PrimaryColor` and `mix` items from the `art` crate: + +Filename: src/main.rs + +``` +use art::kinds::PrimaryColor; +use art::utils::mix; + +fn main() { + let red = PrimaryColor::Red; + let yellow = PrimaryColor::Yellow; + mix(red, yellow); +} +``` + +Listing 14-4: A crate using the `art` crate’s items with its internal structure +exported + +The author of the code in Listing 14-4, which uses the `art` crate, had to +figure out that `PrimaryColor` is in the `kinds` module and `mix` is in the +`utils` module. The module structure of the `art` crate is more relevant to +developers working on the `art` crate than to developers using the `art` crate. +The internal structure that organizes parts of the crate into the `kinds` +module and the `utils` module doesn’t contain any useful information for +someone trying to understand how to use the `art` crate. Instead, the `art` +crate’s module structure causes confusion because developers have to figure out +where to look, and the structure is inconvenient because developers must +specify the module names in the `use` statements. + +To remove the internal organization from the public API, we can modify the +`art` crate code in Listing 14-3 to add `pub use` statements to re-export the +items at the top level, as shown in Listing 14-5: + +Filename: src/lib.rs + +``` +//! # Art +//! +//! A library for modeling artistic concepts. + +pub use self::kinds::PrimaryColor; +pub use self::kinds::SecondaryColor; +pub use self::utils::mix; + +pub mod kinds { + // --snip-- +} + +pub mod utils { + // --snip-- +} +``` + +Listing 14-5: Adding `pub use` statements to re-export items + +The API documentation that `cargo doc` generates for this crate will now list +and link re-exports on the front page, as shown in Figure 14-4, making the +`PrimaryColor` and `SecondaryColor` types and the `mix` function easier to find. + +Rendered documentation for the `art` crate with the re-exports on the front page + +Figure 14-4: The front page of the documentation for `art` +that lists the re-exports + +The `art` crate users can still see and use the internal structure from Listing +14-3 as demonstrated in Listing 14-4, or they can use the more convenient +structure in Listing 14-5, as shown in Listing 14-6: + +Filename: src/main.rs + +``` +use art::mix; +use art::PrimaryColor; + +fn main() { + // --snip-- +} +``` + +Listing 14-6: A program using the re-exported items from the `art` crate + +In cases where there are many nested modules, re-exporting the types at the top +level with `pub use` can make a significant difference in the experience of +people who use the crate. + +Creating a useful public API structure is more of an art than a science, and +you can iterate to find the API that works best for your users. Choosing `pub +use` gives you flexibility in how you structure your crate internally and +decouples that internal structure from what you present to your users. Look at +some of the code of crates you’ve installed to see if their internal structure +differs from their public API. + +### Setting Up a Crates.io Account + +Before you can publish any crates, you need to create an account on +*https://crates.io/* and get an API token. To do so, visit the home page at +*https://crates.io/* and log in via a GitHub account. (The GitHub account is +currently a requirement, but the site might support other ways of creating an +account in the future.) Once you’re logged in, visit your account settings at +*https://crates.io/me/* and retrieve your API key. Then run the `cargo login` +command with your API key, like this: + +``` +$ cargo login abcdefghijklmnopqrstuvwxyz012345 +``` + +This command will inform Cargo of your API token and store it locally in +*~/.cargo/credentials*. Note that this token is a *secret*: do not share it +with anyone else. If you do share it with anyone for any reason, you should +revoke it and generate a new token on *https://crates.io/*. + +### Adding Metadata to a New Crate + +Now that you have an account, let’s say you have a crate you want to publish. +Before publishing, you’ll need to add some metadata to your crate by adding it +to the `[package]` section of the crate’s *Cargo.toml* file. + +Your crate will need a unique name. While you’re working on a crate locally, +you can name a crate whatever you’d like. However, crate names on +*https://crates.io/* are allocated on a first-come, first-served basis. Once a +crate name is taken, no one else can publish a crate with that name. Before +attempting to publish a crate, search for the name you want to use on the site. +If the name has been used by another crate, you will need to find another name +and edit the `name` field in the *Cargo.toml* file under the `[package]` +section to use the new name for publishing, like so: + +Filename: Cargo.toml + +``` +[package] +name = "guessing_game" +``` + +Even if you’ve chosen a unique name, when you run `cargo publish` to publish +the crate at this point, you’ll get a warning and then an error: + +``` +$ cargo publish + Updating crates.io index +warning: manifest has no description, license, license-file, documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +--snip-- +error: failed to publish to registry at https://crates.io + +Caused by: + the remote server responded with an error: missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for how to upload metadata +``` + +The reason is that you’re missing some crucial information: a description and +license are required so people will know what your crate does and under what +terms they can use it. To rectify this error, you need to include this +information in the *Cargo.toml* file. + +Add a description that is just a sentence or two, because it will appear with +your crate in search results. For the `license` field, you need to give a +*license identifier value*. The Linux Foundation’s Software Package Data +Exchange (SPDX) at *http://spdx.org/licenses/* lists the identifiers you can +use for this value. For example, to specify that you’ve licensed your crate +using the MIT License, add the `MIT` identifier: + +Filename: Cargo.toml + +``` +[package] +name = "guessing_game" +license = "MIT" +``` + +If you want to use a license that doesn’t appear in the SPDX, you need to place +the text of that license in a file, include the file in your project, and then +use `license-file` to specify the name of that file instead of using the +`license` key. + +Guidance on which license is appropriate for your project is beyond the scope +of this book. Many people in the Rust community license their projects in the +same way as Rust by using a dual license of `MIT OR Apache-2.0`. This practice +demonstrates that you can also specify multiple license identifiers separated +by `OR` to have multiple licenses for your project. + +With a unique name, the version, your description, and a license added, the +*Cargo.toml* file for a project that is ready to publish might look like this: + +Filename: Cargo.toml + +``` +[package] +name = "guessing_game" +version = "0.1.0" +edition = "2021" +description = "A fun game where you guess what number the computer has chosen." +license = "MIT OR Apache-2.0" + +[dependencies] +``` + +Cargo’s documentation at *https://doc.rust-lang.org/cargo/* describes other +metadata you can specify to ensure others can discover and use your crate more +easily. + +### Publishing to Crates.io + +Now that you’ve created an account, saved your API token, chosen a name for +your crate, and specified the required metadata, you’re ready to publish! +Publishing a crate uploads a specific version to *https://crates.io/* for +others to use. + +Be careful when publishing a crate because a publish is *permanent*. The +version can never be overwritten, and the code cannot be deleted. One major +goal of crates.io is to act as a permanent archive of code so that builds of +all projects that depend on crates from *https://crates.io/* will continue to +work. Allowing version deletions would make fulfilling that goal impossible. +However, there is no limit to the number of crate versions you can publish. + +Run the `cargo publish` command again. It should succeed now: + +``` +$ cargo publish + Updating crates.io index + Packaging guessing_game v0.1.0 (file:///projects/guessing_game) + Verifying guessing_game v0.1.0 (file:///projects/guessing_game) + Compiling guessing_game v0.1.0 +(file:///projects/guessing_game/target/package/guessing_game-0.1.0) + Finished dev [unoptimized + debuginfo] target(s) in 0.19s + Uploading guessing_game v0.1.0 (file:///projects/guessing_game) +``` + +Congratulations! You’ve now shared your code with the Rust community, and +anyone can easily add your crate as a dependency of their project. + +### Publishing a New Version of an Existing Crate + +When you’ve made changes to your crate and are ready to release a new version, +you change the `version` value specified in your *Cargo.toml* file and +republish. Use the Semantic Versioning rules at *http://semver.org/* to decide +what an appropriate next version number is based on the kinds of changes you’ve +made. Then run `cargo publish` to upload the new version. + +### Removing Versions from Crates.io with `cargo yank` + +Although you can’t remove previous versions of a crate, you can prevent any +future projects from adding them as a new dependency. This is useful when a +crate version is broken for one reason or another. In such situations, Cargo +supports *yanking* a crate version. + +Yanking a version prevents new projects from starting to depend on that version +while allowing all existing projects that depend on it to continue to download +and depend on that version. Essentially, a yank means that all projects with a +*Cargo.lock* will not break, and any future *Cargo.lock* files generated will +not use the yanked version. + +To yank a version of a crate, run `cargo yank` and specify which version you +want to yank: + +``` +$ cargo yank --vers 1.0.1 +``` + +By adding `--undo` to the command, you can also undo a yank and allow projects +to start depending on a version again: + +``` +$ cargo yank --vers 1.0.1 --undo +``` + +A yank *does not* delete any code. For example, the yank feature is not +intended for deleting accidentally uploaded secrets. If that happens, you must +reset those secrets immediately. + +## Cargo Workspaces + +In Chapter 12, we built a package that included a binary crate and a library +crate. As your project develops, you might find that the library crate +continues to get bigger and you want to split up your package further into +multiple library crates. In this situation, Cargo offers a feature called +*workspaces* that can help manage multiple related packages that are developed +in tandem. + +### Creating a Workspace + +A *workspace* is a set of packages that share the same *Cargo.lock* and output +directory. Let’s make a project using a workspace—we’ll use trivial code so we +can concentrate on the structure of the workspace. There are multiple ways to +structure a workspace; we’re going to show one common way. We’ll have a +workspace containing a binary and two libraries. The binary, which will provide +the main functionality, will depend on the two libraries. One library will +provide an `add_one` function, and a second library an `add_two` function. +These three crates will be part of the same workspace. We’ll start by creating +a new directory for the workspace: + +``` +$ mkdir add +$ cd add +``` + +Next, in the *add* directory, we create the *Cargo.toml* file that will +configure the entire workspace. This file won’t have a `[package]` section or +the metadata we’ve seen in other *Cargo.toml* files. Instead, it will start +with a `[workspace]` section that will allow us to add members to the workspace +by specifying the path to the package with our binary crate; in this case, +that path is *adder*: + +Filename: Cargo.toml + +``` +[workspace] + +members = [ + "adder", +] +``` + +Next, we’ll create the `adder` binary crate by running `cargo new` within the +*add* directory: + +``` +$ cargo new adder + Created binary (application) `adder` package +``` + +At this point, we can build the workspace by running `cargo build`. The files +in your *add* directory should look like this: + +``` +├── Cargo.lock +├── Cargo.toml +├── adder +│ ├── Cargo.toml +│ └── src +│ └── main.rs +└── target +``` + +The workspace has one *target* directory at the top level for the compiled +artifacts to be placed into; the `adder` package doesn’t have its own *target* +directory. Even if we were to run `cargo build` from inside the *adder* +directory, the compiled artifacts would still end up in *add/target* rather +than *add/adder/target*. Cargo structures the *target* directory in a workspace +like this because the crates in a workspace are meant to depend on each other. +If each crate had its own *target* directory, each crate would have to +recompile each of the other crates in the workspace to have the artifacts in +its own *target* directory. By sharing one *target* directory, the crates can +avoid unnecessary rebuilding. + +### Creating the Second Package in the Workspace + +Next, let’s create another member package in the workspace and call it `add_one`. +Change the top-level *Cargo.toml* to specify the *add_one* path in the +`members` list: + +Filename: Cargo.toml + +``` +[workspace] + +members = [ + "adder", + "add_one", +] +``` + +Then generate a new library crate named `add_one`: + +``` +$ cargo new add_one --lib + Created library `add_one` package +``` + +Your *add* directory should now have these directories and files: + +``` +├── Cargo.lock +├── Cargo.toml +├── add_one +│ ├── Cargo.toml +│ └── src +│ └── lib.rs +├── adder +│ ├── Cargo.toml +│ └── src +│ └── main.rs +└── target +``` + +In the *add_one/src/lib.rs* file, let’s add an `add_one` function: + +Filename: add_one/src/lib.rs + +``` +pub fn add_one(x: i32) -> i32 { + x + 1 +} +``` + +Now that we have another package in the workspace, we can have the `adder` +package with our binary depend on the `add_one` package, that has our +library. First, we’ll need to add a path dependency on `add_one` to +*adder/Cargo.toml*. + +Filename: adder/Cargo.toml + +``` +[dependencies] +add_one = { path = "../add_one" } +``` + +Cargo doesn’t assume that crates in a workspace will depend on each other, so +we need to be explicit about the dependency relationships between the crates. + +Next, let’s use the `add_one` function from the `add_one` crate in the `adder` +crate. Open the *adder/src/main.rs* file and add a `use` line at the top to +bring the new `add_one` library crate into scope. Then change the `main` +function to call the `add_one` function, as in Listing 14-7. + +Filename: adder/src/main.rs + +``` +use add_one; + +fn main() { + let num = 10; + println!( + "Hello, world! {} plus one is {}!", + num, + add_one::add_one(num) + ); +} +``` + +Listing 14-7: Using the `add_one` library crate from the `adder` crate + +Let’s build the workspace by running `cargo build` in the top-level *add* +directory! + +``` +$ cargo build + Compiling add_one v0.1.0 (file:///projects/add/add_one) + Compiling adder v0.1.0 (file:///projects/add/adder) + Finished dev [unoptimized + debuginfo] target(s) in 0.68s +``` + +To run the binary crate from the *add* directory, we can specify which +package in the workspace we want to run by using the `-p` argument and the +package name with `cargo run`: + +``` +$ cargo run -p adder + Finished dev [unoptimized + debuginfo] target(s) in 0.0s + Running `target/debug/adder` +Hello, world! 10 plus one is 11! +``` + +This runs the code in *adder/src/main.rs*, which depends on the `add_one` crate. + +#### Depending on an External Package in a Workspace + +Notice that the workspace has only one *Cargo.lock* file at the top level of +the workspace rather than having a *Cargo.lock* in each crate’s directory. This +ensures that all crates are using the same version of all dependencies. If we +add the `rand` package to the *adder/Cargo.toml* and *add_one/Cargo.toml* +files, Cargo will resolve both of those to one version of `rand` and record +that in the one *Cargo.lock*. Making all crates in the workspace use the same +dependencies means the crates in the workspace will always be compatible with +each other. Let’s add the `rand` crate to the `[dependencies]` section in the +*add_one/Cargo.toml* file to be able to use the `rand` crate in the `add_one` +crate: + +Filename: add_one/Cargo.toml + +``` +[dependencies] +rand = "0.8.3" +``` + +We can now add `use rand;` to the *add_one/src/lib.rs* file, and building the +whole workspace by running `cargo build` in the *add* directory will bring in +and compile the `rand` crate. We will get one warning because we aren’t +referring to the `rand` we brought into scope: + +``` +$ cargo build + Updating crates.io index + Downloaded rand v0.8.3 + --snip-- + Compiling rand v0.8.3 + Compiling add_one v0.1.0 (file:///projects/add/add_one) +warning: unused import: `rand` + --> add_one/src/lib.rs:1:5 + | +1 | use rand; + | ^^^^ + | + = note: `#[warn(unused_imports)]` on by default + +warning: 1 warning emitted + + Compiling adder v0.1.0 (file:///projects/add/adder) + Finished dev [unoptimized + debuginfo] target(s) in 10.18s +``` + +The top-level *Cargo.lock* now contains information about the dependency of +`add_one` on `rand`. However, even though `rand` is used somewhere in the +workspace, we can’t use it in other crates in the workspace unless we add +`rand` to their *Cargo.toml* files as well. For example, if we add `use rand;` +to the *adder/src/main.rs* file for the `adder` package, we’ll get an error: + +``` +$ cargo build + --snip-- + Compiling adder v0.1.0 (file:///projects/add/adder) +error[E0432]: unresolved import `rand` + --> adder/src/main.rs:2:5 + | +2 | use rand; + | ^^^^ no external crate `rand` +``` + +To fix this, edit the *Cargo.toml* file for the `adder` package and indicate +that `rand` is a dependency for it as well. Building the `adder` package will +add `rand` to the list of dependencies for `adder` in *Cargo.lock*, but no +additional copies of `rand` will be downloaded. Cargo has ensured that every +crate in every package in the workspace using the `rand` package will be +using the same version. Using the same version of `rand` across the workspace +saves space because we won’t have multiple copies and ensures that the crates +in the workspace will be compatible with each other. + +#### Adding a Test to a Workspace + +For another enhancement, let’s add a test of the `add_one::add_one` function +within the `add_one` crate: + +Filename: add_one/src/lib.rs + +``` +pub fn add_one(x: i32) -> i32 { + x + 1 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + assert_eq!(3, add_one(2)); + } +} +``` + +Now run `cargo test` in the top-level *add* directory: + +``` +$ cargo test + Compiling add_one v0.1.0 (file:///projects/add/add_one) + Compiling adder v0.1.0 (file:///projects/add/adder) + Finished test [unoptimized + debuginfo] target(s) in 0.27s + Running target/debug/deps/add_one-f0253159197f7841 + +running 1 test +test tests::it_works ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running target/debug/deps/adder-49979ff40686fa8e + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests add_one + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s +``` + +The first section of the output shows that the `it_works` test in the `add_one` +crate passed. The next section shows that zero tests were found in the `adder` +crate, and then the last section shows zero documentation tests were found in +the `add_one` crate. Running `cargo test` in a workspace structured like this +one will run the tests for all the crates in the workspace. + +We can also run tests for one particular crate in a workspace from the +top-level directory by using the `-p` flag and specifying the name of the crate +we want to test: + +``` +$ cargo test -p add_one + Finished test [unoptimized + debuginfo] target(s) in 0.00s + Running target/debug/deps/add_one-b3235fea9a156f74 + +running 1 test +test tests::it_works ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests add_one + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s +``` + +This output shows `cargo test` only ran the tests for the `add_one` crate and +didn’t run the `adder` crate tests. + +If you publish the crates in the workspace to *https://crates.io/*, each crate +in the workspace will need to be published separately. The `cargo publish` +command does not have an `--all` flag or a `-p` flag, so you must change to +each crate’s directory and run `cargo publish` on each crate in the workspace +to publish the crates. + +For additional practice, add an `add_two` crate to this workspace in a similar +way as the `add_one` crate! + +As your project grows, consider using a workspace: it’s easier to understand +smaller, individual components than one big blob of code. Furthermore, keeping +the crates in a workspace can make coordination between them easier if they are +often changed at the same time. + +## Installing Binaries from Crates.io with `cargo install` + +The `cargo install` command allows you to install and use binary crates +locally. This isn’t intended to replace system packages; it’s meant to be a +convenient way for Rust developers to install tools that others have shared on +*https://crates.io/*. Note that you can only install packages that have binary +targets. A *binary target* is the runnable program that is created if the crate +has a *src/main.rs* file or another file specified as a binary, as opposed to a +library target that isn’t runnable on its own but is suitable for including +within other programs. Usually, crates have information in the *README* file +about whether a crate is a library, has a binary target, or both. + +All binaries installed with `cargo install` are stored in the installation +root’s *bin* folder. If you installed Rust using *rustup.rs* and don’t have any +custom configurations, this directory will be *$HOME/.cargo/bin*. Ensure that +directory is in your `$PATH` to be able to run programs you’ve installed with +`cargo install`. + +For example, in Chapter 12 we mentioned that there’s a Rust implementation of +the `grep` tool called `ripgrep` for searching files. If we want to install +`ripgrep`, we can run the following: + +``` +$ cargo install ripgrep + Updating crates.io index + Downloaded ripgrep v11.0.2 + Downloaded 1 crate (243.3 KB) in 0.88s + Installing ripgrep v11.0.2 +--snip-- + Compiling ripgrep v11.0.2 + Finished release [optimized + debuginfo] target(s) in 3m 10s + Installing ~/.cargo/bin/rg + Installed package `ripgrep v11.0.2` (executable `rg`) +``` + +The second-to-last line of the output shows the location and the name of the +installed binary, which in the case of `ripgrep` is `rg`. As long as the +installation directory is in your `$PATH`, as mentioned previously, you can +then run `rg --help` and start using a faster, rustier tool for searching files! + +## Extending Cargo with Custom Commands + +Cargo is designed so you can extend it with new subcommands without having to +modify Cargo. If a binary in your `$PATH` is named `cargo-something`, you can +run it as if it was a Cargo subcommand by running `cargo something`. Custom +commands like this are also listed when you run `cargo --list`. Being able to +use `cargo install` to install extensions and then run them just like the +built-in Cargo tools is a super convenient benefit of Cargo’s design! + +## Summary + +Sharing code with Cargo and *https://crates.io/* is part of what makes the Rust +ecosystem useful for many different tasks. Rust’s standard library is small and +stable, but crates are easy to share, use, and improve on a timeline different +from that of the language. Don’t be shy about sharing code that’s useful to you +on *https://crates.io/*; it’s likely that it will be useful to someone else as +well! diff --git a/src/doc/book/nostarch/chapter15.md b/src/doc/book/nostarch/chapter15.md new file mode 100644 index 0000000000..7645319345 --- /dev/null +++ b/src/doc/book/nostarch/chapter15.md @@ -0,0 +1,2001 @@ + + +[TOC] + +# Smart Pointers + +A *pointer* is a general concept for a variable that contains an address in +memory. This address refers to, or “points at,” some other data. The most +common kind of pointer in Rust is a reference, which you learned about in +Chapter 4. References are indicated by the `&` symbol and borrow the value they +point to. They don’t have any special capabilities other than referring to +data. Also, they don’t have any overhead and are the kind of pointer we use +most often. + +*Smart pointers*, on the other hand, are data structures that not only act like +a pointer but also have additional metadata and capabilities. The concept of +smart pointers isn’t unique to Rust: smart pointers originated in C++ and exist +in other languages as well. In Rust, the different smart pointers defined in +the standard library provide functionality beyond that provided by references. +One example that we’ll explore in this chapter is the *reference counting* +smart pointer type. This pointer enables you to have multiple owners of data by +keeping track of the number of owners and, when no owners remain, cleaning up +the data. + +In Rust, which uses the concept of ownership and borrowing, an additional +difference between references and smart pointers is that references are +pointers that only borrow data; in contrast, in many cases, smart pointers +*own* the data they point to. + +We’ve already encountered a few smart pointers in this book, such as `String` +and `Vec` in Chapter 8, although we didn’t call them smart pointers at the +time. Both these types count as smart pointers because they own some memory and +allow you to manipulate it. They also have metadata (such as their capacity) +and extra capabilities or guarantees (such as with `String` ensuring its data +will always be valid UTF-8). + +Smart pointers are usually implemented using structs. The characteristic that +distinguishes a smart pointer from an ordinary struct is that smart pointers +implement the `Deref` and `Drop` traits. The `Deref` trait allows an instance +of the smart pointer struct to behave like a reference so you can write code +that works with either references or smart pointers. The `Drop` trait allows +you to customize the code that is run when an instance of the smart pointer +goes out of scope. In this chapter, we’ll discuss both traits and demonstrate +why they’re important to smart pointers. + +Given that the smart pointer pattern is a general design pattern used +frequently in Rust, this chapter won’t cover every existing smart pointer. Many +libraries have their own smart pointers, and you can even write your own. We’ll +cover the most common smart pointers in the standard library: + +* `Box` for allocating values on the heap +* `Rc`, a reference counting type that enables multiple ownership +* `Ref` and `RefMut`, accessed through `RefCell`, a type that enforces + the borrowing rules at runtime instead of compile time + +In addition, we’ll cover the *interior mutability* pattern where an immutable +type exposes an API for mutating an interior value. We’ll also discuss +*reference cycles*: how they can leak memory and how to prevent them. + +Let’s dive in! + +## Using `Box` to Point to Data on the Heap + +The most straightforward smart pointer is a *box*, whose type is written +`Box`. Boxes allow you to store data on the heap rather than the stack. What +remains on the stack is the pointer to the heap data. Refer to Chapter 4 to +review the difference between the stack and the heap. + +Boxes don’t have performance overhead, other than storing their data on the +heap instead of on the stack. But they don’t have many extra capabilities +either. You’ll use them most often in these situations: + +* When you have a type whose size can’t be known at compile time and you want + to use a value of that type in a context that requires an exact size +* When you have a large amount of data and you want to transfer ownership but + ensure the data won’t be copied when you do so +* When you want to own a value and you care only that it’s a type that + implements a particular trait rather than being of a specific type + +We’ll demonstrate the first situation in the “Enabling Recursive Types with +Boxes” section. In the second case, transferring ownership of a large amount of +data can take a long time because the data is copied around on the stack. To +improve performance in this situation, we can store the large amount of data on +the heap in a box. Then, only the small amount of pointer data is copied around +on the stack, while the data it references stays in one place on the heap. The +third case is known as a *trait object*, and Chapter 17 devotes an entire +section, “Using Trait Objects That Allow for Values of Different Types,” just +to that topic. So what you learn here you’ll apply again in Chapter 17! + +### Using a `Box` to Store Data on the Heap + +Before we discuss this use case for `Box`, we’ll cover the syntax and how to +interact with values stored within a `Box`. + +Listing 15-1 shows how to use a box to store an `i32` value on the heap: + +Filename: src/main.rs + +``` +fn main() { + let b = Box::new(5); + println!("b = {}", b); +} +``` + +Listing 15-1: Storing an `i32` value on the heap using a box + +We define the variable `b` to have the value of a `Box` that points to the +value `5`, which is allocated on the heap. This program will print `b = 5`; in +this case, we can access the data in the box similar to how we would if this +data were on the stack. Just like any owned value, when a box goes out of +scope, as `b` does at the end of `main`, it will be deallocated. The +deallocation happens for the box (stored on the stack) and the data it points +to (stored on the heap). + +Putting a single value on the heap isn’t very useful, so you won’t use boxes by +themselves in this way very often. Having values like a single `i32` on the +stack, where they’re stored by default, is more appropriate in the majority of +situations. Let’s look at a case where boxes allow us to define types that we +wouldn’t be allowed to if we didn’t have boxes. + +### Enabling Recursive Types with Boxes + +At compile time, Rust needs to know how much space a type takes up. One type +whose size can’t be known at compile time is a *recursive type*, where a value +can have as part of itself another value of the same type. Because this nesting +of values could theoretically continue infinitely, Rust doesn’t know how much +space a value of a recursive type needs. However, boxes have a known size, so +by inserting a box in a recursive type definition, you can have recursive types. + +Let’s explore the *cons list*, which is a data type common in functional +programming languages, as an example of a recursive type. The cons list type +we’ll define is straightforward except for the recursion; therefore, the +concepts in the example we’ll work with will be useful any time you get into +more complex situations involving recursive types. + +#### More Information About the Cons List + +A *cons list* is a data structure that comes from the Lisp programming language +and its dialects. In Lisp, the `cons` function (short for “construct function”) +constructs a new pair from its two arguments, which usually are a single value +and another pair. These pairs containing pairs form a list. + +The cons function concept has made its way into more general functional +programming jargon: “to cons *x* onto *y*” informally means to construct a new +container instance by putting the element *x* at the start of this new +container, followed by the container *y*. + +Each item in a cons list contains two elements: the value of the current item +and the next item. The last item in the list contains only a value called `Nil` +without a next item. A cons list is produced by recursively calling the `cons` +function. The canonical name to denote the base case of the recursion is `Nil`. +Note that this is not the same as the “null” or “nil” concept in Chapter 6, +which is an invalid or absent value. + +Although functional programming languages use cons lists frequently, the cons +list isn’t a commonly used data structure in Rust. Most of the time when you +have a list of items in Rust, `Vec` is a better choice to use. Other, more +complex recursive data types *are* useful in various situations, but by +starting with the cons list, we can explore how boxes let us define a recursive +data type without much distraction. + +Listing 15-2 contains an enum definition for a cons list. Note that this code +won’t compile yet because the `List` type doesn’t have a known size, which +we’ll demonstrate. + +Filename: src/main.rs + +``` +enum List { + Cons(i32, List), + Nil, +} +``` + +Listing 15-2: The first attempt at defining an enum to represent a cons list +data structure of `i32` values + +> Note: We’re implementing a cons list that holds only `i32` values for the +> purposes of this example. We could have implemented it using generics, as we +> discussed in Chapter 10, to define a cons list type that could store values of +> any type. + +Using the `List` type to store the list `1, 2, 3` would look like the code in +Listing 15-3: + +Filename: src/main.rs + +``` +use crate::List::{Cons, Nil}; + +fn main() { + let list = Cons(1, Cons(2, Cons(3, Nil))); +} +``` + +Listing 15-3: Using the `List` enum to store the list `1, 2, 3` + +The first `Cons` value holds `1` and another `List` value. This `List` value is +another `Cons` value that holds `2` and another `List` value. This `List` value +is one more `Cons` value that holds `3` and a `List` value, which is finally +`Nil`, the non-recursive variant that signals the end of the list. + +If we try to compile the code in Listing 15-3, we get the error shown in +Listing 15-4: + +``` +error[E0072]: recursive type `List` has infinite size + --> src/main.rs:1:1 + | +1 | enum List { + | ^^^^^^^^^ recursive type has infinite size +2 | Cons(i32, List), + | ---- recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable +``` + +Listing 15-4: The error we get when attempting to define a recursive enum + +The error shows this type “has infinite size.” The reason is that we’ve defined +`List` with a variant that is recursive: it holds another value of itself +directly. As a result, Rust can’t figure out how much space it needs to store a +`List` value. Let’s break down why we get this error a bit. First, let’s look +at how Rust decides how much space it needs to store a value of a non-recursive +type. + +#### Computing the Size of a Non-Recursive Type + +Recall the `Message` enum we defined in Listing 6-2 when we discussed enum +definitions in Chapter 6: + +``` +enum Message { + Quit, + Move { x: i32, y: i32 }, + Write(String), + ChangeColor(i32, i32, i32), +} +``` + +To determine how much space to allocate for a `Message` value, Rust goes +through each of the variants to see which variant needs the most space. Rust +sees that `Message::Quit` doesn’t need any space, `Message::Move` needs enough +space to store two `i32` values, and so forth. Because only one variant will be +used, the most space a `Message` value will need is the space it would take to +store the largest of its variants. + +Contrast this with what happens when Rust tries to determine how much space a +recursive type like the `List` enum in Listing 15-2 needs. The compiler starts +by looking at the `Cons` variant, which holds a value of type `i32` and a value +of type `List`. Therefore, `Cons` needs an amount of space equal to the size of +an `i32` plus the size of a `List`. To figure out how much memory the `List` +type needs, the compiler looks at the variants, starting with the `Cons` +variant. The `Cons` variant holds a value of type `i32` and a value of type +`List`, and this process continues infinitely, as shown in Figure 15-1. + +An infinite Cons list + +Figure 15-1: An infinite `List` consisting of infinite `Cons` variants + +#### Using `Box` to Get a Recursive Type with a Known Size + +Rust can’t figure out how much space to allocate for recursively defined types, +so the compiler gives the error in Listing 15-4. But the error does include +this helpful suggestion: + +``` +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable +``` + +In this suggestion, “indirection” means that instead of storing a value +directly, we’ll change the data structure to store the value indirectly by +storing a pointer to the value instead. + +Because a `Box` is a pointer, Rust always knows how much space a `Box` +needs: a pointer’s size doesn’t change based on the amount of data it’s +pointing to. This means we can put a `Box` inside the `Cons` variant instead +of another `List` value directly. The `Box` will point to the next `List` +value that will be on the heap rather than inside the `Cons` variant. +Conceptually, we still have a list, created with lists “holding” other lists, +but this implementation is now more like placing the items next to one another +rather than inside one another. + +We can change the definition of the `List` enum in Listing 15-2 and the usage +of the `List` in Listing 15-3 to the code in Listing 15-5, which will compile: + +Filename: src/main.rs + +``` +enum List { + Cons(i32, Box), + Nil, +} + +use crate::List::{Cons, Nil}; + +fn main() { + let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); +} +``` + +Listing 15-5: Definition of `List` that uses `Box` in order to have a known +size + +The `Cons` variant will need the size of an `i32` plus the space to store the +box’s pointer data. The `Nil` variant stores no values, so it needs less space +than the `Cons` variant. We now know that any `List` value will take up the +size of an `i32` plus the size of a box’s pointer data. By using a box, we’ve +broken the infinite, recursive chain, so the compiler can figure out the size +it needs to store a `List` value. Figure 15-2 shows what the `Cons` variant +looks like now. + +A finite Cons list + +Figure 15-2: A `List` that is not infinitely sized because `Cons` holds a `Box` + +Boxes provide only the indirection and heap allocation; they don’t have any +other special capabilities, like those we’ll see with the other smart pointer +types. They also don’t have any performance overhead that these special +capabilities incur, so they can be useful in cases like the cons list where the +indirection is the only feature we need. We’ll look at more use cases for boxes +in Chapter 17, too. + +The `Box` type is a smart pointer because it implements the `Deref` trait, +which allows `Box` values to be treated like references. When a `Box` +value goes out of scope, the heap data that the box is pointing to is cleaned +up as well because of the `Drop` trait implementation. Let’s explore these two +traits in more detail. These two traits will be even more important to the +functionality provided by the other smart pointer types we’ll discuss in the +rest of this chapter. + +## Treating Smart Pointers Like Regular References with the `Deref` Trait + +Implementing the `Deref` trait allows you to customize the behavior of the +*dereference operator*, `*` (as opposed to the multiplication or glob +operator). By implementing `Deref` in such a way that a smart pointer can be +treated like a regular reference, you can write code that operates on +references and use that code with smart pointers too. + +Let’s first look at how the dereference operator works with regular references. +Then we’ll try to define a custom type that behaves like `Box`, and see why +the dereference operator doesn’t work like a reference on our newly defined +type. We’ll explore how implementing the `Deref` trait makes it possible for +smart pointers to work in ways similar to references. Then we’ll look at +Rust’s *deref coercion* feature and how it lets us work with either references +or smart pointers. + +> Note: there’s one big difference between the `MyBox` type we’re about to +> build and the real `Box`: our version will not store its data on the heap. +> We are focusing this example on `Deref`, so where the data is actually stored +> is less important than the pointer-like behavior. + +### Following the Pointer to the Value with the Dereference Operator + +A regular reference is a type of pointer, and one way to think of a pointer is +as an arrow to a value stored somewhere else. In Listing 15-6, we create a +reference to an `i32` value and then use the dereference operator to follow the +reference to the data: + +Filename: src/main.rs + +``` +fn main() { + [1] let x = 5; + [2] let y = &x; + + [3] assert_eq!(5, x); + [4] assert_eq!(5, *y); +} +``` + +Listing 15-6: Using the dereference operator to follow a reference to an `i32` +value + +The variable `x` holds an `i32` value, `5` [1]. We set `y` equal to a reference +to `x` [2]. We can assert that `x` is equal to `5` [3]. However, if we want to +make an assertion about the value in `y`, we have to use `*y` to follow the +reference to the value it’s pointing to (hence *dereference*) [4]. Once we +dereference `y`, we have access to the integer value `y` is pointing to that we +can compare with `5`. + +If we tried to write `assert_eq!(5, y);` instead, we would get this compilation +error: + +``` +error[E0277]: can't compare `{integer}` with `&{integer}` + --> src/main.rs:6:5 + | +6 | assert_eq!(5, y); + | ^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}` + | + = help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}` +``` + +Comparing a number and a reference to a number isn’t allowed because they’re +different types. We must use the dereference operator to follow the reference +to the value it’s pointing to. + +### Using `Box` Like a Reference + +We can rewrite the code in Listing 15-6 to use a `Box` instead of a +reference; the dereference operator will work as shown in Listing 15-7: + +Filename: src/main.rs + +``` +fn main() { + let x = 5; + [1] let y = Box::new(x); + + assert_eq!(5, x); + [2] assert_eq!(5, *y); +} +``` + +Listing 15-7: Using the dereference operator on a `Box` + +The main difference between Listing 15-7 and Listing 15-6 is that here we set +`y` to be an instance of a box pointing to a copied value of `x` rather than a +reference pointing to the value of `x` [1]. In the last assertion [2], we can +use the dereference operator to follow the box’s pointer in the same way that +we did when `y` was a reference. Next, we’ll explore what is special about +`Box` that enables us to use the dereference operator by defining our own +box type. + +### Defining Our Own Smart Pointer + +Let’s build a smart pointer similar to the `Box` type provided by the +standard library to experience how smart pointers behave differently from +references by default. Then we’ll look at how to add the ability to use the +dereference operator. + +The `Box` type is ultimately defined as a tuple struct with one element, so +Listing 15-8 defines a `MyBox` type in the same way. We’ll also define a +`new` function to match the `new` function defined on `Box`. + +Filename: src/main.rs + +``` +[1] struct MyBox(T); + +impl MyBox { + [2] fn new(x: T) -> MyBox { + [3] MyBox(x) + } +} +``` + +Listing 15-8: Defining a `MyBox` type + +We define a struct named `MyBox` and declare a generic parameter `T` [1], +because we want our type to hold values of any type. The `MyBox` type is a +tuple struct with one element of type `T`. The `MyBox::new` function takes one +parameter of type `T` [2] and returns a `MyBox` instance that holds the value +passed in [3]. + +Let’s try adding the `main` function in Listing 15-7 to Listing 15-8 and +changing it to use the `MyBox` type we’ve defined instead of `Box`. The +code in Listing 15-9 won’t compile because Rust doesn’t know how to dereference +`MyBox`. + +Filename: src/main.rs + +``` +fn main() { + let x = 5; + let y = MyBox::new(x); + + assert_eq!(5, x); + assert_eq!(5, *y); +} +``` + +Listing 15-9: Attempting to use `MyBox` in the same way we used references +and `Box` + +Here’s the resulting compilation error: + +``` +error[E0614]: type `MyBox<{integer}>` cannot be dereferenced + --> src/main.rs:14:19 + | +14 | assert_eq!(5, *y); + | ^^ +``` + +Our `MyBox` type can’t be dereferenced because we haven’t implemented that +ability on our type. To enable dereferencing with the `*` operator, we +implement the `Deref` trait. + +### Treating a Type Like a Reference by Implementing the `Deref` Trait + +As discussed in the “Implementing a Trait on a Type” section of Chapter 10, to +implement a trait, we need to provide implementations for the trait’s required +methods. The `Deref` trait, provided by the standard library, requires us to +implement one method named `deref` that borrows `self` and returns a reference +to the inner data. Listing 15-10 contains an implementation of `Deref` to add +to the definition of `MyBox`: + +Filename: src/main.rs + +``` +use std::ops::Deref; + +impl Deref for MyBox { + [1] type Target = T; + + fn deref(&self) -> &Self::Target { + [2] &self.0 + } +} +``` + +Listing 15-10: Implementing `Deref` on `MyBox` + +The `type Target = T;` syntax [1] defines an associated type for the `Deref` +trait to use. Associated types are a slightly different way of declaring a +generic parameter, but you don’t need to worry about them for now; we’ll cover +them in more detail in Chapter 19. + +We fill in the body of the `deref` method with `&self.0` so `deref` returns a +reference to the value we want to access with the `*` operator [2]. Recall from +the “Using Tuple Structs without Named Fields to Create Different Types” +section of Chapter 5 that `.0` accesses the first value in a tuple struct. The +`main` function in Listing 15-9 that calls `*` on the `MyBox` value now +compiles, and the assertions pass! + +Without the `Deref` trait, the compiler can only dereference `&` references. +The `deref` method gives the compiler the ability to take a value of any type +that implements `Deref` and call the `deref` method to get a `&` reference that +it knows how to dereference. + +When we entered `*y` in Listing 15-9, behind the scenes Rust actually ran this +code: + +``` +*(y.deref()) +``` + +Rust substitutes the `*` operator with a call to the `deref` method and then a +plain dereference so we don’t have to think about whether or not we need to +call the `deref` method. This Rust feature lets us write code that functions +identically whether we have a regular reference or a type that implements +`Deref`. + +The reason the `deref` method returns a reference to a value, and that the plain +dereference outside the parentheses in `*(y.deref())` is still necessary, is the +ownership system. If the `deref` method returned the value directly instead of +a reference to the value, the value would be moved out of `self`. We don’t want +to take ownership of the inner value inside `MyBox` in this case or in most +cases where we use the dereference operator. + +Note that the `*` operator is replaced with a call to the `deref` method and +then a call to the `*` operator just once, each time we use a `*` in our code. +Because the substitution of the `*` operator does not recurse infinitely, we +end up with data of type `i32`, which matches the `5` in `assert_eq!` in +Listing 15-9. + +### Implicit Deref Coercions with Functions and Methods + +*Deref coercion* is a convenience that Rust performs on arguments to functions +and methods. Deref coercion works only on types that implement the `Deref` +trait. Deref coercion converts a reference to such a type into a reference to +another type. For example, deref coercion can convert `&String` to `&str` +because `String` implements the `Deref` trait such that it returns `&str`. +Deref coercion happens automatically when we pass a reference to a particular +type’s value as an argument to a function or method that doesn’t match the +parameter type in the function or method definition. A sequence of calls to the +`deref` method converts the type we provided into the type the parameter needs. + +Deref coercion was added to Rust so that programmers writing function and +method calls don’t need to add as many explicit references and dereferences +with `&` and `*`. The deref coercion feature also lets us write more code that +can work for either references or smart pointers. + +To see deref coercion in action, let’s use the `MyBox` type we defined in +Listing 15-8 as well as the implementation of `Deref` that we added in Listing +15-10. Listing 15-11 shows the definition of a function that has a string slice +parameter: + +Filename: src/main.rs + +``` +fn hello(name: &str) { + println!("Hello, {}!", name); +} +``` + +Listing 15-11: A `hello` function that has the parameter `name` of type `&str` + +We can call the `hello` function with a string slice as an argument, such as +`hello("Rust");` for example. Deref coercion makes it possible to call `hello` +with a reference to a value of type `MyBox`, as shown in Listing 15-12: + +Filename: src/main.rs + +``` +fn main() { + let m = MyBox::new(String::from("Rust")); + hello(&m); +} +``` + +Listing 15-12: Calling `hello` with a reference to a `MyBox` value, +which works because of deref coercion + +Here we’re calling the `hello` function with the argument `&m`, which is a +reference to a `MyBox` value. Because we implemented the `Deref` trait +on `MyBox` in Listing 15-10, Rust can turn `&MyBox` into `&String` +by calling `deref`. The standard library provides an implementation of `Deref` +on `String` that returns a string slice, and this is in the API documentation +for `Deref`. Rust calls `deref` again to turn the `&String` into `&str`, which +matches the `hello` function’s definition. + +If Rust didn’t implement deref coercion, we would have to write the code in +Listing 15-13 instead of the code in Listing 15-12 to call `hello` with a value +of type `&MyBox`. + +Filename: src/main.rs + +``` +fn main() { + let m = MyBox::new(String::from("Rust")); + hello(&(*m)[..]); +} +``` + +Listing 15-13: The code we would have to write if Rust didn’t have deref +coercion + +The `(*m)` dereferences the `MyBox` into a `String`. Then the `&` and +`[..]` take a string slice of the `String` that is equal to the whole string to +match the signature of `hello`. The code without deref coercions is harder to +read, write, and understand with all of these symbols involved. Deref coercion +allows Rust to handle these conversions for us automatically. + +When the `Deref` trait is defined for the types involved, Rust will analyze the +types and use `Deref::deref` as many times as necessary to get a reference to +match the parameter’s type. The number of times that `Deref::deref` needs to be +inserted is resolved at compile time, so there is no runtime penalty for taking +advantage of deref coercion! + +### How Deref Coercion Interacts with Mutability + +Similar to how you use the `Deref` trait to override the `*` operator on +immutable references, you can use the `DerefMut` trait to override the `*` +operator on mutable references. + +Rust does deref coercion when it finds types and trait implementations in three +cases: + +* From `&T` to `&U` when `T: Deref` +* From `&mut T` to `&mut U` when `T: DerefMut` +* From `&mut T` to `&U` when `T: Deref` + +The first two cases are the same except for mutability. The first case states +that if you have a `&T`, and `T` implements `Deref` to some type `U`, you can +get a `&U` transparently. The second case states that the same deref coercion +happens for mutable references. + +The third case is trickier: Rust will also coerce a mutable reference to an +immutable one. But the reverse is *not* possible: immutable references will +never coerce to mutable references. Because of the borrowing rules, if you have +a mutable reference, that mutable reference must be the only reference to that +data (otherwise, the program wouldn’t compile). Converting one mutable +reference to one immutable reference will never break the borrowing rules. +Converting an immutable reference to a mutable reference would require that the +initial immutable reference is the only immutable reference to that data, but +the borrowing rules don’t guarantee that. Therefore, Rust can’t make the +assumption that converting an immutable reference to a mutable reference is +possible. + +## Running Code on Cleanup with the `Drop` Trait + +The second trait important to the smart pointer pattern is `Drop`, which lets +you customize what happens when a value is about to go out of scope. You can +provide an implementation for the `Drop` trait on any type, and the code you +specify can be used to release resources like files or network connections. +We’re introducing `Drop` in the context of smart pointers because the +functionality of the `Drop` trait is almost always used when implementing a +smart pointer. For example, when a `Box` is dropped it will deallocate the +space on the heap that the box points to. + +In some languages, the programmer must call code to free memory or resources +every time they finish using an instance of a smart pointer. If they forget, +the system might become overloaded and crash. In Rust, you can specify that a +particular bit of code be run whenever a value goes out of scope, and the +compiler will insert this code automatically. As a result, you don’t need to be +careful about placing cleanup code everywhere in a program that an instance of +a particular type is finished with—you still won’t leak resources! + +Specify the code to run when a value goes out of scope by implementing the +`Drop` trait. The `Drop` trait requires you to implement one method named +`drop` that takes a mutable reference to `self`. To see when Rust calls `drop`, +let’s implement `drop` with `println!` statements for now. + +Listing 15-14 shows a `CustomSmartPointer` struct whose only custom +functionality is that it will print `Dropping CustomSmartPointer!` when the +instance goes out of scope. This example demonstrates when Rust runs the `drop` +function. + +Filename: src/main.rs + +``` +struct CustomSmartPointer { + data: String, +} + +[1] impl Drop for CustomSmartPointer { + fn drop(&mut self) { + [2] println!("Dropping CustomSmartPointer with data `{}`!", self.data); + } +} + +fn main() { + [3] let c = CustomSmartPointer { + data: String::from("my stuff"), + }; + [4] let d = CustomSmartPointer { + data: String::from("other stuff"), + }; + [5] println!("CustomSmartPointers created."); +[6] } +``` + +Listing 15-14: A `CustomSmartPointer` struct that implements the `Drop` trait +where we would put our cleanup code + +The `Drop` trait is included in the prelude, so we don’t need to bring it into +scope. We implement the `Drop` trait on `CustomSmartPointer` [1] and provide an +implementation for the `drop` method that calls `println!` [2]. The body of the +`drop` function is where you would place any logic that you wanted to run when +an instance of your type goes out of scope. We’re printing some text here to +demonstrate when Rust will call `drop`. + +In `main`, we create two instances of `CustomSmartPointer` [3][4] and then +print `CustomSmartPointers created` [5]. At the end of `main` [6], our +instances of `CustomSmartPointer` will go out of scope, and Rust will call the +code we put in the `drop` method [2], printing our final message. Note that we +didn’t need to call the `drop` method explicitly. + +When we run this program, we’ll see the following output: + +``` +CustomSmartPointers created. +Dropping CustomSmartPointer with data `other stuff`! +Dropping CustomSmartPointer with data `my stuff`! +``` + +Rust automatically called `drop` for us when our instances went out of scope, +calling the code we specified. Variables are dropped in the reverse order of +their creation, so `d` was dropped before `c`. This example gives you a visual +guide to how the `drop` method works; usually you would specify the cleanup +code that your type needs to run rather than a print message. + +### Dropping a Value Early with `std::mem::drop` + +Unfortunately, it’s not straightforward to disable the automatic `drop` +functionality. Disabling `drop` isn’t usually necessary; the whole point of the +`Drop` trait is that it’s taken care of automatically. Occasionally, however, +you might want to clean up a value early. One example is when using smart +pointers that manage locks: you might want to force the `drop` method that +releases the lock so that other code in the same scope can acquire the lock. +Rust doesn’t let you call the `Drop` trait’s `drop` method manually; instead +you have to call the `std::mem::drop` function provided by the standard library +if you want to force a value to be dropped before the end of its scope. + +If we try to call the `Drop` trait’s `drop` method manually by modifying the +`main` function from Listing 15-14, as shown in Listing 15-15, we’ll get a +compiler error: + +Filename: src/main.rs + +``` +fn main() { + let c = CustomSmartPointer { + data: String::from("some data"), + }; + println!("CustomSmartPointer created."); + c.drop(); + println!("CustomSmartPointer dropped before the end of main."); +} +``` + +Listing 15-15: Attempting to call the `drop` method from the `Drop` trait +manually to clean up early + +When we try to compile this code, we’ll get this error: + +``` +error[E0040]: explicit use of destructor method + --> src/main.rs:16:7 + | +16 | c.drop(); + | --^^^^-- + | | | + | | explicit destructor calls not allowed +``` + +This error message states that we’re not allowed to explicitly call `drop`. The +error message uses the term *destructor*, which is the general programming term +for a function that cleans up an instance. A *destructor* is analogous to a +*constructor*, which creates an instance. The `drop` function in Rust is one +particular destructor. + +Rust doesn’t let us call `drop` explicitly because Rust would still +automatically call `drop` on the value at the end of `main`. This would be a +*double free* error because Rust would be trying to clean up the same value +twice. + +We can’t disable the automatic insertion of `drop` when a value goes out of +scope, and we can’t call the `drop` method explicitly. So, if we need to force +a value to be cleaned up early, we can use the `std::mem::drop` function. + +The `std::mem::drop` function is different from the `drop` method in the `Drop` +trait. We call it by passing the value we want to force to be dropped early as +an argument. The function is in the prelude, so we can modify `main` in Listing +15-15 to call the `drop` function, as shown in Listing 15-16: + +Filename: src/main.rs + +``` +fn main() { + let c = CustomSmartPointer { + data: String::from("some data"), + }; + println!("CustomSmartPointer created."); + drop(c); + println!("CustomSmartPointer dropped before the end of main."); +} +``` + +Listing 15-16: Calling `std::mem::drop` to explicitly drop a value before it +goes out of scope + +Running this code will print the following: + +``` +CustomSmartPointer created. +Dropping CustomSmartPointer with data `some data`! +CustomSmartPointer dropped before the end of main. +``` + +The text ```Dropping CustomSmartPointer with data `some data`!``` is printed +between the `CustomSmartPointer created.` and `CustomSmartPointer dropped +before the end of main.` text, showing that the `drop` method code is called to +drop `c` at that point. + +You can use code specified in a `Drop` trait implementation in many ways to +make cleanup convenient and safe: for instance, you could use it to create your +own memory allocator! With the `Drop` trait and Rust’s ownership system, you +don’t have to remember to clean up because Rust does it automatically. + +You also don’t have to worry about problems resulting from accidentally +cleaning up values still in use: the ownership system that makes sure +references are always valid also ensures that `drop` gets called only once when +the value is no longer being used. + +Now that we’ve examined `Box` and some of the characteristics of smart +pointers, let’s look at a few other smart pointers defined in the standard +library. + +## `Rc`, the Reference Counted Smart Pointer + +In the majority of cases, ownership is clear: you know exactly which variable +owns a given value. However, there are cases when a single value might have +multiple owners. For example, in graph data structures, multiple edges might +point to the same node, and that node is conceptually owned by all of the edges +that point to it. A node shouldn’t be cleaned up unless it doesn’t have any +edges pointing to it. + +To enable multiple ownership, Rust has a type called `Rc`, which is an +abbreviation for *reference counting*. The `Rc` type keeps track of the +number of references to a value to determine whether or not the value is still +in use. If there are zero references to a value, the value can be cleaned up +without any references becoming invalid. + +Imagine `Rc` as a TV in a family room. When one person enters to watch TV, +they turn it on. Others can come into the room and watch the TV. When the last +person leaves the room, they turn off the TV because it’s no longer being used. +If someone turns off the TV while others are still watching it, there would be +uproar from the remaining TV watchers! + +We use the `Rc` type when we want to allocate some data on the heap for +multiple parts of our program to read and we can’t determine at compile time +which part will finish using the data last. If we knew which part would finish +last, we could just make that part the data’s owner, and the normal ownership +rules enforced at compile time would take effect. + +Note that `Rc` is only for use in single-threaded scenarios. When we discuss +concurrency in Chapter 16, we’ll cover how to do reference counting in +multithreaded programs. + +### Using `Rc` to Share Data + +Let’s return to our cons list example in Listing 15-5. Recall that we defined +it using `Box`. This time, we’ll create two lists that both share ownership +of a third list. Conceptually, this looks similar to Figure 15-3: + +Two lists that share ownership of a third list + +Figure 15-3: Two lists, `b` and `c`, sharing ownership of a third list, `a` + +We’ll create list `a` that contains 5 and then 10. Then we’ll make two more +lists: `b` that starts with 3 and `c` that starts with 4. Both `b` and `c` +lists will then continue on to the first `a` list containing 5 and 10. In other +words, both lists will share the first list containing 5 and 10. + +Trying to implement this scenario using our definition of `List` with `Box` +won’t work, as shown in Listing 15-17: + +Filename: src/main.rs + +``` +enum List { + Cons(i32, Box), + Nil, +} + +use crate::List::{Cons, Nil}; + +fn main() { + let a = Cons(5, Box::new(Cons(10, Box::new(Nil)))); +[1] let b = Cons(3, Box::new(a)); +[2] let c = Cons(4, Box::new(a)); +} +``` + +Listing 15-17: Demonstrating we’re not allowed to have two lists using `Box` +that try to share ownership of a third list + +When we compile this code, we get this error: + +``` +error[E0382]: use of moved value: `a` + --> src/main.rs:11:30 + | +9 | let a = Cons(5, Box::new(Cons(10, Box::new(Nil)))); + | - move occurs because `a` has type `List`, which does not implement the `Copy` trait +10 | let b = Cons(3, Box::new(a)); + | - value moved here +11 | let c = Cons(4, Box::new(a)); + | ^ value used here after move +``` + +The `Cons` variants own the data they hold, so when we create the `b` list [1], +`a` is moved into `b` and `b` owns `a`. Then, when we try to use `a` again when +creating `c` [2], we’re not allowed to because `a` has been moved. + +We could change the definition of `Cons` to hold references instead, but then +we would have to specify lifetime parameters. By specifying lifetime +parameters, we would be specifying that every element in the list will live at +least as long as the entire list. This is the case for the elements and lists +in Listing 15-17, but not in every scenario. + +Instead, we’ll change our definition of `List` to use `Rc` in place of +`Box`, as shown in Listing 15-18. Each `Cons` variant will now hold a value +and an `Rc` pointing to a `List`. When we create `b`, instead of taking +ownership of `a`, we’ll clone the `Rc` that `a` is holding, thereby +increasing the number of references from one to two and letting `a` and `b` +share ownership of the data in that `Rc`. We’ll also clone `a` when +creating `c`, increasing the number of references from two to three. Every time +we call `Rc::clone`, the reference count to the data within the `Rc` will +increase, and the data won’t be cleaned up unless there are zero references to +it. + +Filename: src/main.rs + +``` +enum List { + Cons(i32, Rc), + Nil, +} + +use crate::List::{Cons, Nil}; +[1] use std::rc::Rc; + +fn main() { +[2] let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); +[3] let b = Cons(3, Rc::clone(&a)); +[4] let c = Cons(4, Rc::clone(&a)); +} +``` + +Listing 15-18: A definition of `List` that uses `Rc` + +We need to add a `use` statement to bring `Rc` into scope [1] because it’s +not in the prelude. In `main`, we create the list holding 5 and 10 and store it +in a new `Rc` in `a` [2]. Then when we create `b` [3] and `c` [4], we +call the `Rc::clone` function and pass a reference to the `Rc` in `a` as +an argument. + +We could have called `a.clone()` rather than `Rc::clone(&a)`, but Rust’s +convention is to use `Rc::clone` in this case. The implementation of +`Rc::clone` doesn’t make a deep copy of all the data like most types’ +implementations of `clone` do. The call to `Rc::clone` only increments the +reference count, which doesn’t take much time. Deep copies of data can take a +lot of time. By using `Rc::clone` for reference counting, we can visually +distinguish between the deep-copy kinds of clones and the kinds of clones that +increase the reference count. When looking for performance problems in the +code, we only need to consider the deep-copy clones and can disregard calls to +`Rc::clone`. + +### Cloning an `Rc` Increases the Reference Count + +Let’s change our working example in Listing 15-18 so we can see the reference +counts changing as we create and drop references to the `Rc` in `a`. + +In Listing 15-19, we’ll change `main` so it has an inner scope around list `c`; +then we can see how the reference count changes when `c` goes out of scope. + +Filename: src/main.rs + +``` +fn main() { + let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); + println!("count after creating a = {}", Rc::strong_count(&a)); + let b = Cons(3, Rc::clone(&a)); + println!("count after creating b = {}", Rc::strong_count(&a)); + { + let c = Cons(4, Rc::clone(&a)); + println!("count after creating c = {}", Rc::strong_count(&a)); + } + println!("count after c goes out of scope = {}", Rc::strong_count(&a)); +} +``` + +Listing 15-19: Printing the reference count + +At each point in the program where the reference count changes, we print the +reference count, which we can get by calling the `Rc::strong_count` function. +This function is named `strong_count` rather than `count` because the `Rc` +type also has a `weak_count`; we’ll see what `weak_count` is used for in the +“Preventing Reference Cycles: Turning an `Rc` into a `Weak`” section. + +This code prints the following: + +``` +count after creating a = 1 +count after creating b = 2 +count after creating c = 3 +count after c goes out of scope = 2 +``` + +We can see that the `Rc` in `a` has an initial reference count of 1; then +each time we call `clone`, the count goes up by 1. When `c` goes out of scope, +the count goes down by 1. We don’t have to call a function to decrease the +reference count like we have to call `Rc::clone` to increase the reference +count: the implementation of the `Drop` trait decreases the reference count +automatically when an `Rc` value goes out of scope. + +What we can’t see in this example is that when `b` and then `a` go out of scope +at the end of `main`, the count is then 0, and the `Rc` is cleaned up +completely at that point. Using `Rc` allows a single value to have +multiple owners, and the count ensures that the value remains valid as long as +any of the owners still exist. + +Via immutable references, `Rc` allows you to share data between multiple +parts of your program for reading only. If `Rc` allowed you to have multiple +mutable references too, you might violate one of the borrowing rules discussed +in Chapter 4: multiple mutable borrows to the same place can cause data races +and inconsistencies. But being able to mutate data is very useful! In the next +section, we’ll discuss the interior mutability pattern and the `RefCell` +type that you can use in conjunction with an `Rc` to work with this +immutability restriction. + +## `RefCell` and the Interior Mutability Pattern + +*Interior mutability* is a design pattern in Rust that allows you to mutate +data even when there are immutable references to that data; normally, this +action is disallowed by the borrowing rules. To mutate data, the pattern uses +`unsafe` code inside a data structure to bend Rust’s usual rules that govern +mutation and borrowing. We haven’t yet covered unsafe code; we will in Chapter +19. We can use types that use the interior mutability pattern when we can +ensure that the borrowing rules will be followed at runtime, even though the +compiler can’t guarantee that. The `unsafe` code involved is then wrapped in a +safe API, and the outer type is still immutable. + +Let’s explore this concept by looking at the `RefCell` type that follows the +interior mutability pattern. + +### Enforcing Borrowing Rules at Runtime with `RefCell` + +Unlike `Rc`, the `RefCell` type represents single ownership over the data +it holds. So, what makes `RefCell` different from a type like `Box`? +Recall the borrowing rules you learned in Chapter 4: + +* At any given time, you can have *either* (but not both of) one mutable + reference or any number of immutable references. +* References must always be valid. + +With references and `Box`, the borrowing rules’ invariants are enforced at +compile time. With `RefCell`, these invariants are enforced *at runtime*. +With references, if you break these rules, you’ll get a compiler error. With +`RefCell`, if you break these rules, your program will panic and exit. + +The advantages of checking the borrowing rules at compile time are that errors +will be caught sooner in the development process, and there is no impact on +runtime performance because all the analysis is completed beforehand. For those +reasons, checking the borrowing rules at compile time is the best choice in the +majority of cases, which is why this is Rust’s default. + +The advantage of checking the borrowing rules at runtime instead is that +certain memory-safe scenarios are then allowed, whereas they are disallowed by +the compile-time checks. Static analysis, like the Rust compiler, is inherently +conservative. Some properties of code are impossible to detect by analyzing the +code: the most famous example is the Halting Problem, which is beyond the scope +of this book but is an interesting topic to research. + +Because some analysis is impossible, if the Rust compiler can’t be sure the +code complies with the ownership rules, it might reject a correct program; in +this way, it’s conservative. If Rust accepted an incorrect program, users +wouldn’t be able to trust in the guarantees Rust makes. However, if Rust +rejects a correct program, the programmer will be inconvenienced, but nothing +catastrophic can occur. The `RefCell` type is useful when you’re sure your +code follows the borrowing rules but the compiler is unable to understand and +guarantee that. + +Similar to `Rc`, `RefCell` is only for use in single-threaded scenarios +and will give you a compile-time error if you try using it in a multithreaded +context. We’ll talk about how to get the functionality of `RefCell` in a +multithreaded program in Chapter 16. + +Here is a recap of the reasons to choose `Box`, `Rc`, or `RefCell`: + +* `Rc` enables multiple owners of the same data; `Box` and `RefCell` + have single owners. +* `Box` allows immutable or mutable borrows checked at compile time; `Rc` + allows only immutable borrows checked at compile time; `RefCell` allows + immutable or mutable borrows checked at runtime. +* Because `RefCell` allows mutable borrows checked at runtime, you can + mutate the value inside the `RefCell` even when the `RefCell` is + immutable. + +Mutating the value inside an immutable value is the *interior mutability* +pattern. Let’s look at a situation in which interior mutability is useful and +examine how it’s possible. + +### Interior Mutability: A Mutable Borrow to an Immutable Value + +A consequence of the borrowing rules is that when you have an immutable value, +you can’t borrow it mutably. For example, this code won’t compile: + +``` +fn main() { + let x = 5; + let y = &mut x; +} +``` + +If you tried to compile this code, you’d get the following error: + +``` +error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable + --> src/main.rs:3:13 + | +2 | let x = 5; + | - help: consider changing this to be mutable: `mut x` +3 | let y = &mut x; + | ^^^^^^ cannot borrow as mutable +``` + +However, there are situations in which it would be useful for a value to mutate +itself in its methods but appear immutable to other code. Code outside the +value’s methods would not be able to mutate the value. Using `RefCell` is +one way to get the ability to have interior mutability. But `RefCell` +doesn’t get around the borrowing rules completely: the borrow checker in the +compiler allows this interior mutability, and the borrowing rules are checked +at runtime instead. If you violate the rules, you’ll get a `panic!` instead of +a compiler error. + +Let’s work through a practical example where we can use `RefCell` to mutate +an immutable value and see why that is useful. + +#### A Use Case for Interior Mutability: Mock Objects + +A *test double* is the general programming concept for a type used in place of +another type during testing. *Mock objects* are specific types of test doubles +that record what happens during a test so you can assert that the correct +actions took place. + +Rust doesn’t have objects in the same sense as other languages have objects, +and Rust doesn’t have mock object functionality built into the standard library +as some other languages do. However, you can definitely create a struct that +will serve the same purposes as a mock object. + +Here’s the scenario we’ll test: we’ll create a library that tracks a value +against a maximum value and sends messages based on how close to the maximum +value the current value is. This library could be used to keep track of a +user’s quota for the number of API calls they’re allowed to make, for example. + +Our library will only provide the functionality of tracking how close to the +maximum a value is and what the messages should be at what times. Applications +that use our library will be expected to provide the mechanism for sending the +messages: the application could put a message in the application, send an +email, send a text message, or something else. The library doesn’t need to know +that detail. All it needs is something that implements a trait we’ll provide +called `Messenger`. Listing 15-20 shows the library code: + +Filename: src/lib.rs + +``` +pub trait Messenger { +[1] fn send(&self, msg: &str); +} + +pub struct LimitTracker<'a, T: Messenger> { + messenger: &'a T, + value: usize, + max: usize, +} + +impl<'a, T> LimitTracker<'a, T> +where + T: Messenger, +{ + pub fn new(messenger: &T, max: usize) -> LimitTracker { + LimitTracker { + messenger, + value: 0, + max, + } + } + +[2] pub fn set_value(&mut self, value: usize) { + self.value = value; + + let percentage_of_max = self.value as f64 / self.max as f64; + + if percentage_of_max >= 1.0 { + self.messenger.send("Error: You are over your quota!"); + } else if percentage_of_max >= 0.9 { + self.messenger + .send("Urgent warning: You've used up over 90% of your quota!"); + } else if percentage_of_max >= 0.75 { + self.messenger + .send("Warning: You've used up over 75% of your quota!"); + } + } +} +``` + +Listing 15-20: A library to keep track of how close a value is to a maximum +value and warn when the value is at certain levels + +One important part of this code is that the `Messenger` trait has one method +called `send` that takes an immutable reference to `self` and the text of the +message [1]. This trait is the interface our mock object needs to implement so +that the mock can be used in the same way a real object is. The other important +part is that we want to test the behavior of the `set_value` method on the +`LimitTracker` [2]. We can change what we pass in for the `value` parameter, +but `set_value` doesn’t return anything for us to make assertions on. We want +to be able to say that if we create a `LimitTracker` with something that +implements the `Messenger` trait and a particular value for `max`, when we pass +different numbers for `value`, the messenger is told to send the appropriate +messages. + +We need a mock object that, instead of sending an email or text message when we +call `send`, will only keep track of the messages it’s told to send. We can +create a new instance of the mock object, create a `LimitTracker` that uses the +mock object, call the `set_value` method on `LimitTracker`, and then check that +the mock object has the messages we expect. Listing 15-21 shows an attempt to +implement a mock object to do just that, but the borrow checker won’t allow it: + +Filename: src/lib.rs + +``` +#[cfg(test)] +mod tests { + use super::*; + + [1] struct MockMessenger { + [2] sent_messages: Vec, + } + + impl MockMessenger { + [3] fn new() -> MockMessenger { + MockMessenger { + sent_messages: vec![], + } + } + } + + [4] impl Messenger for MockMessenger { + fn send(&self, message: &str) { + [5] self.sent_messages.push(String::from(message)); + } + } + + #[test] + [6] fn it_sends_an_over_75_percent_warning_message() { + let mock_messenger = MockMessenger::new(); + let mut limit_tracker = LimitTracker::new(&mock_messenger, 100); + + limit_tracker.set_value(80); + + assert_eq!(mock_messenger.sent_messages.len(), 1); + } +} +``` + +Listing 15-21: An attempt to implement a `MockMessenger` that isn’t allowed by +the borrow checker + +This test code defines a `MockMessenger` struct [1] that has a `sent_messages` +field with a `Vec` of `String` values [2] to keep track of the messages it’s +told to send. We also define an associated function `new` [3] to make it +convenient to create new `MockMessenger` values that start with an empty list +of messages. We then implement the `Messenger` trait for `MockMessenger` [4] so +we can give a `MockMessenger` to a `LimitTracker`. In the definition of the +`send` method [5], we take the message passed in as a parameter and store it in +the `MockMessenger` list of `sent_messages`. + +In the test, we’re testing what happens when the `LimitTracker` is told to set +`value` to something that is more than 75 percent of the `max` value [6]. +First, we create a new `MockMessenger`, which will start with an empty list of +messages. Then we create a new `LimitTracker` and give it a reference to the +new `MockMessenger` and a `max` value of 100. We call the `set_value` method on +the `LimitTracker` with a value of 80, which is more than 75 percent of 100. +Then we assert that the list of messages that the `MockMessenger` is keeping +track of should now have one message in it. + +However, there’s one problem with this test, as shown here: + +``` +error[E0596]: cannot borrow `self.sent_messages` as mutable, as it is behind a `&` reference + --> src/lib.rs:58:13 + | +2 | fn send(&self, msg: &str); + | ----- help: consider changing that to be a mutable reference: `&mut self` +... +58 | self.sent_messages.push(String::from(message)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable +``` + +We can’t modify the `MockMessenger` to keep track of the messages, because the +`send` method takes an immutable reference to `self`. We also can’t take the +suggestion from the error text to use `&mut self` instead, because then the +signature of `send` wouldn’t match the signature in the `Messenger` trait +definition (feel free to try and see what error message you get). + +This is a situation in which interior mutability can help! We’ll store the +`sent_messages` within a `RefCell`, and then the `send` method will be +able to modify `sent_messages` to store the messages we’ve seen. Listing 15-22 +shows what that looks like: + +Filename: src/lib.rs + +``` +#[cfg(test)] +mod tests { + use super::*; + use std::cell::RefCell; + + struct MockMessenger { + [1] sent_messages: RefCell>, + } + + impl MockMessenger { + fn new() -> MockMessenger { + MockMessenger { + sent_messages: RefCell::new(vec![]) [2], + } + } + } + + impl Messenger for MockMessenger { + fn send(&self, message: &str) { + [3] self.sent_messages.borrow_mut().push(String::from(message)); + } + } + + #[test] + fn it_sends_an_over_75_percent_warning_message() { + // --snip-- + + [4] assert_eq!(mock_messenger.sent_messages.borrow().len(), 1); + } +} +``` + +Listing 15-22: Using `RefCell` to mutate an inner value while the outer +value is considered immutable + +The `sent_messages` field is now of type `RefCell>` [1] instead of +`Vec`. In the `new` function, we create a new `RefCell>` +instance around the empty vector [2]. + +For the implementation of the `send` method, the first parameter is still an +immutable borrow of `self`, which matches the trait definition. We call +`borrow_mut` on the `RefCell>` in `self.sent_messages` [3] to get a +mutable reference to the value inside the `RefCell>`, which is +the vector. Then we can call `push` on the mutable reference to the vector to +keep track of the messages sent during the test. + +The last change we have to make is in the assertion: to see how many items are +in the inner vector, we call `borrow` on the `RefCell>` to get an +immutable reference to the vector [4]. + +Now that you’ve seen how to use `RefCell`, let’s dig into how it works! + +#### Keeping Track of Borrows at Runtime with `RefCell` + +When creating immutable and mutable references, we use the `&` and `&mut` +syntax, respectively. With `RefCell`, we use the `borrow` and `borrow_mut` +methods, which are part of the safe API that belongs to `RefCell`. The +`borrow` method returns the smart pointer type `Ref`, and `borrow_mut` +returns the smart pointer type `RefMut`. Both types implement `Deref`, so we +can treat them like regular references. + +The `RefCell` keeps track of how many `Ref` and `RefMut` smart +pointers are currently active. Every time we call `borrow`, the `RefCell` +increases its count of how many immutable borrows are active. When a `Ref` +value goes out of scope, the count of immutable borrows goes down by one. Just +like the compile-time borrowing rules, `RefCell` lets us have many immutable +borrows or one mutable borrow at any point in time. + +If we try to violate these rules, rather than getting a compiler error as we +would with references, the implementation of `RefCell` will panic at +runtime. Listing 15-23 shows a modification of the implementation of `send` in +Listing 15-22. We’re deliberately trying to create two mutable borrows active +for the same scope to illustrate that `RefCell` prevents us from doing this +at runtime. + +Filename: src/lib.rs + +``` + impl Messenger for MockMessenger { + fn send(&self, message: &str) { + let mut one_borrow = self.sent_messages.borrow_mut(); + let mut two_borrow = self.sent_messages.borrow_mut(); + + one_borrow.push(String::from(message)); + two_borrow.push(String::from(message)); + } + } +``` + +Listing 15-23: Creating two mutable references in the same scope to see that +`RefCell` will panic + +We create a variable `one_borrow` for the `RefMut` smart pointer returned +from `borrow_mut`. Then we create another mutable borrow in the same way in the +variable `two_borrow`. This makes two mutable references in the same scope, +which isn’t allowed. When we run the tests for our library, the code in Listing +15-23 will compile without any errors, but the test will fail: + +``` +---- tests::it_sends_an_over_75_percent_warning_message stdout ---- +thread 'main' panicked at 'already borrowed: BorrowMutError', src/lib.rs:60:53 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +``` + +Notice that the code panicked with the message `already borrowed: +BorrowMutError`. This is how `RefCell` handles violations of the borrowing +rules at runtime. + +Catching borrowing errors at runtime rather than compile time means that you +would find a mistake in your code later in the development process and possibly +not until your code was deployed to production. Also, your code would incur a +small runtime performance penalty as a result of keeping track of the borrows +at runtime rather than compile time. However, using `RefCell` makes it +possible to write a mock object that can modify itself to keep track of the +messages it has seen while you’re using it in a context where only immutable +values are allowed. You can use `RefCell` despite its trade-offs to get more +functionality than regular references provide. + +### Having Multiple Owners of Mutable Data by Combining `Rc` and `RefCell` + +A common way to use `RefCell` is in combination with `Rc`. Recall that +`Rc` lets you have multiple owners of some data, but it only gives immutable +access to that data. If you have an `Rc` that holds a `RefCell`, you can +get a value that can have multiple owners *and* that you can mutate! + +For example, recall the cons list example in Listing 15-18 where we used +`Rc` to allow multiple lists to share ownership of another list. Because +`Rc` holds only immutable values, we can’t change any of the values in the +list once we’ve created them. Let’s add in `RefCell` to gain the ability to +change the values in the lists. Listing 15-24 shows that by using a +`RefCell` in the `Cons` definition, we can modify the value stored in all +the lists: + +Filename: src/main.rs + +``` +#[derive(Debug)] +enum List { + Cons(Rc>, Rc), + Nil, +} + +use crate::List::{Cons, Nil}; +use std::cell::RefCell; +use std::rc::Rc; + +fn main() { + [1] let value = Rc::new(RefCell::new(5)); + + [2] let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); + + let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a)); + let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a)); + + [3] *value.borrow_mut() += 10; + + println!("a after = {:?}", a); + println!("b after = {:?}", b); + println!("c after = {:?}", c); +} +``` + +Listing 15-24: Using `Rc>` to create a `List` that we can mutate + +We create a value that is an instance of `Rc>` and store it in a +variable named `value` [1] so we can access it directly later. Then we create a +`List` in `a` with a `Cons` variant that holds `value` [2]. We need to clone +`value` so both `a` and `value` have ownership of the inner `5` value rather +than transferring ownership from `value` to `a` or having `a` borrow from +`value`. + +We wrap the list `a` in an `Rc` so when we create lists `b` and `c`, they +can both refer to `a`, which is what we did in Listing 15-18. + +After we’ve created the lists in `a`, `b`, and `c`, we add 10 to the value in +`value` [3]. We do this by calling `borrow_mut` on `value`, which uses the +automatic dereferencing feature we discussed in Chapter 5 (see the section +“Where’s the `->` Operator?”) to dereference the `Rc` to the inner +`RefCell` value. The `borrow_mut` method returns a `RefMut` smart +pointer, and we use the dereference operator on it and change the inner value. + +When we print `a`, `b`, and `c`, we can see that they all have the modified +value of 15 rather than 5: + +``` +a after = Cons(RefCell { value: 15 }, Nil) +b after = Cons(RefCell { value: 3 }, Cons(RefCell { value: 15 }, Nil)) +c after = Cons(RefCell { value: 4 }, Cons(RefCell { value: 15 }, Nil)) +``` + +This technique is pretty neat! By using `RefCell`, we have an outwardly +immutable `List` value. But we can use the methods on `RefCell` that provide +access to its interior mutability so we can modify our data when we need to. +The runtime checks of the borrowing rules protect us from data races, and it’s +sometimes worth trading a bit of speed for this flexibility in our data +structures. + +The standard library has other types that provide interior mutability, such as +`Cell`, which is similar except that instead of giving references to the +inner value, the value is copied in and out of the `Cell`. There’s also +`Mutex`, which offers interior mutability that’s safe to use across threads; +we’ll discuss its use in Chapter 16. Check out the standard library docs for +more details on the differences between these types. + +## Reference Cycles Can Leak Memory + +Rust’s memory safety guarantees make it difficult, but not impossible, to +accidentally create memory that is never cleaned up (known as a *memory leak*). +Preventing memory leaks entirely is not one of Rust’s guarantees, meaning +memory leaks are memory safe in Rust. We can see that Rust allows memory leaks +by using `Rc` and `RefCell`: it’s possible to create references where +items refer to each other in a cycle. This creates memory leaks because the +reference count of each item in the cycle will never reach 0, and the values +will never be dropped. + +### Creating a Reference Cycle + +Let’s look at how a reference cycle might happen and how to prevent it, +starting with the definition of the `List` enum and a `tail` method in Listing +15-25: + +Filename: src/main.rs + +``` +use crate::List::{Cons, Nil}; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug)] +enum List { + [1] Cons(i32, RefCell>), + Nil, +} + +impl List { + [2] fn tail(&self) -> Option<&RefCell>> { + match self { + Cons(_, item) => Some(item), + Nil => None, + } + } +} +``` + +Listing 15-25: A cons list definition that holds a `RefCell` so we can +modify what a `Cons` variant is referring to + +We’re using another variation of the `List` definition from Listing 15-5. The +second element in the `Cons` variant is now `RefCell>` [1], meaning +that instead of having the ability to modify the `i32` value as we did in +Listing 15-24, we want to modify which `List` value a `Cons` variant is +pointing to. We’re also adding a `tail` method [2] to make it convenient for us +to access the second item if we have a `Cons` variant. + +In Listing 15-26, we’re adding a `main` function that uses the definitions in +Listing 15-25. This code creates a list in `a` and a list in `b` that points to +the list in `a`. Then it modifies the list in `a` to point to `b`, creating a +reference cycle. There are `println!` statements along the way to show what the +reference counts are at various points in this process. + +Filename: src/main.rs + +``` +fn main() { + [1] let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil)))); + + println!("a initial rc count = {}", Rc::strong_count(&a)); + println!("a next item = {:?}", a.tail()); + + [2] let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a)))); + + println!("a rc count after b creation = {}", Rc::strong_count(&a)); + println!("b initial rc count = {}", Rc::strong_count(&b)); + println!("b next item = {:?}", b.tail()); + + [3] if let Some(link) = a.tail() { + [4] *link.borrow_mut() = Rc::clone(&b); + } + + println!("b rc count after changing a = {}", Rc::strong_count(&b)); + println!("a rc count after changing a = {}", Rc::strong_count(&a)); + + // Uncomment the next line to see that we have a cycle; + // it will overflow the stack + // println!("a next item = {:?}", a.tail()); +} +``` + +Listing 15-26: Creating a reference cycle of two `List` values pointing to each +other + +We create an `Rc` instance holding a `List` value in the variable `a` +with an initial list of `5, Nil` [1]. We then create an `Rc` instance +holding another `List` value in the variable `b` that contains the value 10 and +points to the list in `a` [2]. + +We modify `a` so it points to `b` instead of `Nil`, creating a cycle. We do +that by using the `tail` method to get a reference to the `RefCell>` +in `a`, which we put in the variable `link` [3]. Then we use the `borrow_mut` +method on the `RefCell>` to change the value inside from an `Rc` +that holds a `Nil` value to the `Rc` in `b` [4]. + +When we run this code, keeping the last `println!` commented out for the +moment, we’ll get this output: + +``` +a initial rc count = 1 +a next item = Some(RefCell { value: Nil }) +a rc count after b creation = 2 +b initial rc count = 1 +b next item = Some(RefCell { value: Cons(5, RefCell { value: Nil }) }) +b rc count after changing a = 2 +a rc count after changing a = 2 +``` + +The reference count of the `Rc` instances in both `a` and `b` are 2 after +we change the list in `a` to point to `b`. At the end of `main`, Rust drops the +variable `b`, which decreases the reference count of the `Rc` instance +from 2 to 1. The memory that `Rc` has on the heap won’t be dropped at +this point, because its reference count is 1, not 0. Then Rust drops `a`, which +decreases the reference count of the `a` `Rc` instance from 2 to 1 as +well. This instance’s memory can’t be dropped either, because the other +`Rc` instance still refers to it. The memory allocated to the list will +remain uncollected forever. To visualize this reference cycle, we’ve created a +diagram in Figure 15-4. + +Reference cycle of lists + +Figure 15-4: A reference cycle of lists `a` and `b` pointing to each other + +If you uncomment the last `println!` and run the program, Rust will try to +print this cycle with `a` pointing to `b` pointing to `a` and so forth until it +overflows the stack. + +In this case, right after we create the reference cycle, the program ends. The +consequences of this cycle aren’t very dire. However, if a more complex program +allocated lots of memory in a cycle and held onto it for a long time, the +program would use more memory than it needed and might overwhelm the system, +causing it to run out of available memory. + +Creating reference cycles is not easily done, but it’s not impossible either. +If you have `RefCell` values that contain `Rc` values or similar nested +combinations of types with interior mutability and reference counting, you must +ensure that you don’t create cycles; you can’t rely on Rust to catch them. +Creating a reference cycle would be a logic bug in your program that you should +use automated tests, code reviews, and other software development practices to +minimize. + +Another solution for avoiding reference cycles is reorganizing your data +structures so that some references express ownership and some references don’t. +As a result, you can have cycles made up of some ownership relationships and +some non-ownership relationships, and only the ownership relationships affect +whether or not a value can be dropped. In Listing 15-25, we always want `Cons` +variants to own their list, so reorganizing the data structure isn’t possible. +Let’s look at an example using graphs made up of parent nodes and child nodes +to see when non-ownership relationships are an appropriate way to prevent +reference cycles. + +### Preventing Reference Cycles: Turning an `Rc` into a `Weak` + +So far, we’ve demonstrated that calling `Rc::clone` increases the +`strong_count` of an `Rc` instance, and an `Rc` instance is only cleaned +up if its `strong_count` is 0. You can also create a *weak reference* to the +value within an `Rc` instance by calling `Rc::downgrade` and passing a +reference to the `Rc`. When you call `Rc::downgrade`, you get a smart +pointer of type `Weak`. Instead of increasing the `strong_count` in the +`Rc` instance by 1, calling `Rc::downgrade` increases the `weak_count` by 1. +The `Rc` type uses `weak_count` to keep track of how many `Weak` +references exist, similar to `strong_count`. The difference is the `weak_count` +doesn’t need to be 0 for the `Rc` instance to be cleaned up. + +Strong references are how you can share ownership of an `Rc` instance. Weak +references don’t express an ownership relationship. They won’t cause a +reference cycle because any cycle involving some weak references will be broken +once the strong reference count of values involved is 0. + +Because the value that `Weak` references might have been dropped, to do +anything with the value that a `Weak` is pointing to, you must make sure the +value still exists. Do this by calling the `upgrade` method on a `Weak` +instance, which will return an `Option>`. You’ll get a result of `Some` +if the `Rc` value has not been dropped yet and a result of `None` if the +`Rc` value has been dropped. Because `upgrade` returns an `Option>`, +Rust will ensure that the `Some` case and the `None` case are handled, and +there won’t be an invalid pointer. + +As an example, rather than using a list whose items know only about the next +item, we’ll create a tree whose items know about their children items *and* +their parent items. + +#### Creating a Tree Data Structure: a `Node` with Child Nodes + +To start, we’ll build a tree with nodes that know about their child nodes. +We’ll create a struct named `Node` that holds its own `i32` value as well as +references to its children `Node` values: + +Filename: src/main.rs + +``` +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug)] +struct Node { + value: i32, + children: RefCell>>, +} +``` + +We want a `Node` to own its children, and we want to share that ownership with +variables so we can access each `Node` in the tree directly. To do this, we +define the `Vec` items to be values of type `Rc`. We also want to +modify which nodes are children of another node, so we have a `RefCell` in +`children` around the `Vec>`. + +Next, we’ll use our struct definition and create one `Node` instance named +`leaf` with the value 3 and no children, and another instance named `branch` +with the value 5 and `leaf` as one of its children, as shown in Listing 15-27: + +Filename: src/main.rs + +``` +fn main() { + let leaf = Rc::new(Node { + value: 3, + children: RefCell::new(vec![]), + }); + + let branch = Rc::new(Node { + value: 5, + children: RefCell::new(vec![Rc::clone(&leaf)]), + }); +} +``` + +Listing 15-27: Creating a `leaf` node with no children and a `branch` node with +`leaf` as one of its children + +We clone the `Rc` in `leaf` and store that in `branch`, meaning the +`Node` in `leaf` now has two owners: `leaf` and `branch`. We can get from +`branch` to `leaf` through `branch.children`, but there’s no way to get from +`leaf` to `branch`. The reason is that `leaf` has no reference to `branch` and +doesn’t know they’re related. We want `leaf` to know that `branch` is its +parent. We’ll do that next. + +#### Adding a Reference from a Child to Its Parent + +To make the child node aware of its parent, we need to add a `parent` field to +our `Node` struct definition. The trouble is in deciding what the type of +`parent` should be. We know it can’t contain an `Rc`, because that would +create a reference cycle with `leaf.parent` pointing to `branch` and +`branch.children` pointing to `leaf`, which would cause their `strong_count` +values to never be 0. + +Thinking about the relationships another way, a parent node should own its +children: if a parent node is dropped, its child nodes should be dropped as +well. However, a child should not own its parent: if we drop a child node, the +parent should still exist. This is a case for weak references! + +So instead of `Rc`, we’ll make the type of `parent` use `Weak`, +specifically a `RefCell>`. Now our `Node` struct definition looks +like this: + +Filename: src/main.rs + +``` +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +#[derive(Debug)] +struct Node { + value: i32, + parent: RefCell>, + children: RefCell>>, +} +``` + +A node will be able to refer to its parent node but doesn’t own its parent. +In Listing 15-28, we update `main` to use this new definition so the `leaf` +node will have a way to refer to its parent, `branch`: + +Filename: src/main.rs + +``` +fn main() { + let leaf = Rc::new(Node { + value: 3, + [1] parent: RefCell::new(Weak::new()), + children: RefCell::new(vec![]), + }); + + [2] println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); + + let branch = Rc::new(Node { + value: 5, + [3] parent: RefCell::new(Weak::new()), + children: RefCell::new(vec![Rc::clone(&leaf)]), + }); + + [4] *leaf.parent.borrow_mut() = Rc::downgrade(&branch); + + [5] println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); +} +``` + +Listing 15-28: A `leaf` node with a weak reference to its parent node `branch` + +Creating the `leaf` node looks similar to how creating the `leaf` node looked +in Listing 15-27 with the exception of the `parent` field: `leaf` starts out +without a parent, so we create a new, empty `Weak` reference instance [1]. + +At this point, when we try to get a reference to the parent of `leaf` by using +the `upgrade` method, we get a `None` value. We see this in the output from the +first `println!` statement [2]: + +``` +leaf parent = None +``` + +When we create the `branch` node, it will also have a new `Weak` +reference in the `parent` field [3], because `branch` doesn’t have a parent +node. We still have `leaf` as one of the children of `branch`. Once we have the +`Node` instance in `branch`, we can modify `leaf` to give it a `Weak` +reference to its parent [4]. We use the `borrow_mut` method on the +`RefCell>` in the `parent` field of `leaf`, and then we use the +`Rc::downgrade` function to create a `Weak` reference to `branch` from +the `Rc` in `branch.` + +When we print the parent of `leaf` again [5], this time we’ll get a `Some` +variant holding `branch`: now `leaf` can access its parent! When we print +`leaf`, we also avoid the cycle that eventually ended in a stack overflow like +we had in Listing 15-26; the `Weak` references are printed as `(Weak)`: + +``` +leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) }, +children: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) }, +children: RefCell { value: [] } }] } }) +``` + +The lack of infinite output indicates that this code didn’t create a reference +cycle. We can also tell this by looking at the values we get from calling +`Rc::strong_count` and `Rc::weak_count`. + +#### Visualizing Changes to `strong_count` and `weak_count` + +Let’s look at how the `strong_count` and `weak_count` values of the `Rc` +instances change by creating a new inner scope and moving the creation of +`branch` into that scope. By doing so, we can see what happens when `branch` is +created and then dropped when it goes out of scope. The modifications are shown +in Listing 15-29: + +Filename: src/main.rs + +``` +fn main() { + let leaf = Rc::new(Node { + value: 3, + parent: RefCell::new(Weak::new()), + children: RefCell::new(vec![]), + }); + + [1] println!( + "leaf strong = {}, weak = {}", + Rc::strong_count(&leaf), + Rc::weak_count(&leaf), + ); + + [2] { + let branch = Rc::new(Node { + value: 5, + parent: RefCell::new(Weak::new()), + children: RefCell::new(vec![Rc::clone(&leaf)]), + }); + + *leaf.parent.borrow_mut() = Rc::downgrade(&branch); + + [3] println!( + "branch strong = {}, weak = {}", + Rc::strong_count(&branch), + Rc::weak_count(&branch), + ); + + [4] println!( + "leaf strong = {}, weak = {}", + Rc::strong_count(&leaf), + Rc::weak_count(&leaf), + ); + [5] } + + [6] println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); + [7] println!( + "leaf strong = {}, weak = {}", + Rc::strong_count(&leaf), + Rc::weak_count(&leaf), + ); +} +``` + +Listing 15-29: Creating `branch` in an inner scope and examining strong and +weak reference counts + +After `leaf` is created, its `Rc` has a strong count of 1 and a weak +count of 0 [1]. In the inner scope [2], we create `branch` and associate it +with `leaf`, at which point when we print the counts [3], the `Rc` in +`branch` will have a strong count of 1 and a weak count of 1 (for `leaf.parent` +pointing to `branch` with a `Weak`). When we print the counts in `leaf` +[4], we’ll see it will have a strong count of 2, because `branch` now has a +clone of the `Rc` of `leaf` stored in `branch.children`, but will still +have a weak count of 0. + +When the inner scope ends [5], `branch` goes out of scope and the strong count +of the `Rc` decreases to 0, so its `Node` is dropped. The weak count of 1 +from `leaf.parent` has no bearing on whether or not `Node` is dropped, so we +don’t get any memory leaks! + +If we try to access the parent of `leaf` after the end of the scope, we’ll get +`None` again [6]. At the end of the program [7], the `Rc` in `leaf` has a +strong count of 1 and a weak count of 0, because the variable `leaf` is now the +only reference to the `Rc` again. + +All of the logic that manages the counts and value dropping is built into +`Rc` and `Weak` and their implementations of the `Drop` trait. By +specifying that the relationship from a child to its parent should be a +`Weak` reference in the definition of `Node`, you’re able to have parent +nodes point to child nodes and vice versa without creating a reference cycle +and memory leaks. + +## Summary + +This chapter covered how to use smart pointers to make different guarantees and +trade-offs from those Rust makes by default with regular references. The +`Box` type has a known size and points to data allocated on the heap. The +`Rc` type keeps track of the number of references to data on the heap so +that data can have multiple owners. The `RefCell` type with its interior +mutability gives us a type that we can use when we need an immutable type but +need to change an inner value of that type; it also enforces the borrowing +rules at runtime instead of at compile time. + +Also discussed were the `Deref` and `Drop` traits, which enable a lot of the +functionality of smart pointers. We explored reference cycles that can cause +memory leaks and how to prevent them using `Weak`. + +If this chapter has piqued your interest and you want to implement your own +smart pointers, check out “The Rustonomicon” at +*https://doc.rust-lang.org/stable/nomicon/* for more useful information. + +Next, we’ll talk about concurrency in Rust. You’ll even learn about a few new +smart pointers. diff --git a/src/doc/book/nostarch/chapter16.md b/src/doc/book/nostarch/chapter16.md new file mode 100644 index 0000000000..1c852395c7 --- /dev/null +++ b/src/doc/book/nostarch/chapter16.md @@ -0,0 +1,1204 @@ + + +[TOC] + +# Fearless Concurrency + +Handling concurrent programming safely and efficiently is another of Rust’s +major goals. *Concurrent programming*, where different parts of a program +execute independently, and *parallel programming*, where different parts of a +program execute at the same time, are becoming increasingly important as more +computers take advantage of their multiple processors. Historically, +programming in these contexts has been difficult and error prone: Rust hopes to +change that. + +Initially, the Rust team thought that ensuring memory safety and preventing +concurrency problems were two separate challenges to be solved with different +methods. Over time, the team discovered that the ownership and type systems are +a powerful set of tools to help manage memory safety *and* concurrency +problems! By leveraging ownership and type checking, many concurrency errors +are compile-time errors in Rust rather than runtime errors. Therefore, rather +than making you spend lots of time trying to reproduce the exact circumstances +under which a runtime concurrency bug occurs, incorrect code will refuse to +compile and present an error explaining the problem. As a result, you can fix +your code while you’re working on it rather than potentially after it has been +shipped to production. We’ve nicknamed this aspect of Rust *fearless* +*concurrency*. Fearless concurrency allows you to write code that is free of +subtle bugs and is easy to refactor without introducing new bugs. + +> Note: For simplicity’s sake, we’ll refer to many of the problems as +> *concurrent* rather than being more precise by saying *concurrent and/or +> parallel*. If this book were about concurrency and/or parallelism, we’d be +> more specific. For this chapter, please mentally substitute *concurrent +> and/or parallel* whenever we use *concurrent*. + +Many languages are dogmatic about the solutions they offer for handling +concurrent problems. For example, Erlang has elegant functionality for +message-passing concurrency but has only obscure ways to share state between +threads. Supporting only a subset of possible solutions is a reasonable +strategy for higher-level languages, because a higher-level language promises +benefits from giving up some control to gain abstractions. However, lower-level +languages are expected to provide the solution with the best performance in any +given situation and have fewer abstractions over the hardware. Therefore, Rust +offers a variety of tools for modeling problems in whatever way is appropriate +for your situation and requirements. + +Here are the topics we’ll cover in this chapter: + +* How to create threads to run multiple pieces of code at the same time +* *Message-passing* concurrency, where channels send messages between threads +* *Shared-state* concurrency, where multiple threads have access to some piece + of data +* The `Sync` and `Send` traits, which extend Rust’s concurrency guarantees to + user-defined types as well as types provided by the standard library + +## Using Threads to Run Code Simultaneously + +In most current operating systems, an executed program’s code is run in a +*process*, and the operating system manages multiple processes at once. Within +your program, you can also have independent parts that run simultaneously. The +features that run these independent parts are called *threads*. + +Splitting the computation in your program into multiple threads can improve +performance because the program does multiple tasks at the same time, but it +also adds complexity. Because threads can run simultaneously, there’s no +inherent guarantee about the order in which parts of your code on different +threads will run. This can lead to problems, such as: + +* Race conditions, where threads are accessing data or resources in an + inconsistent order +* Deadlocks, where two threads are waiting for each other to finish using a + resource the other thread has, preventing both threads from continuing +* Bugs that happen only in certain situations and are hard to reproduce and fix + reliably + +Rust attempts to mitigate the negative effects of using threads, but +programming in a multithreaded context still takes careful thought and requires +a code structure that is different from that in programs running in a single +thread. + +Programming languages implement threads in a few different ways. Many operating +systems provide an API for creating new threads. This model where a language +calls the operating system APIs to create threads is sometimes called *1:1*, +meaning one operating system thread per one language thread. The Rust standard +library only provides an implementation of 1:1 threading; there are crates that +implement other models of threading that make different tradeoffs. + +### Creating a New Thread with `spawn` + +To create a new thread, we call the `thread::spawn` function and pass it a +closure (we talked about closures in Chapter 13) containing the code we want to +run in the new thread. The example in Listing 16-1 prints some text from a main +thread and other text from a new thread: + +Filename: src/main.rs + +``` +use std::thread; +use std::time::Duration; + +fn main() { + thread::spawn(|| { + for i in 1..10 { + println!("hi number {} from the spawned thread!", i); + thread::sleep(Duration::from_millis(1)); + } + }); + + for i in 1..5 { + println!("hi number {} from the main thread!", i); + thread::sleep(Duration::from_millis(1)); + } +} +``` + +Listing 16-1: Creating a new thread to print one thing while the main thread +prints something else + +Note that with this function, the new thread will be stopped when the main +thread ends, whether or not it has finished running. The output from this +program might be a little different every time, but it will look similar to the +following: + +``` +hi number 1 from the main thread! +hi number 1 from the spawned thread! +hi number 2 from the main thread! +hi number 2 from the spawned thread! +hi number 3 from the main thread! +hi number 3 from the spawned thread! +hi number 4 from the main thread! +hi number 4 from the spawned thread! +hi number 5 from the spawned thread! +``` + +The calls to `thread::sleep` force a thread to stop its execution for a short +duration, allowing a different thread to run. The threads will probably take +turns, but that isn’t guaranteed: it depends on how your operating system +schedules the threads. In this run, the main thread printed first, even though +the print statement from the spawned thread appears first in the code. And even +though we told the spawned thread to print until `i` is 9, it only got to 5 +before the main thread shut down. + +If you run this code and only see output from the main thread, or don’t see any +overlap, try increasing the numbers in the ranges to create more opportunities +for the operating system to switch between the threads. + +### Waiting for All Threads to Finish Using `join` Handles + +The code in Listing 16-1 not only stops the spawned thread prematurely most of +the time due to the main thread ending, but also can’t guarantee that the +spawned thread will get to run at all. The reason is that there is no guarantee +on the order in which threads run! + +We can fix the problem of the spawned thread not getting to run, or not getting +to run completely, by saving the return value of `thread::spawn` in a variable. +The return type of `thread::spawn` is `JoinHandle`. A `JoinHandle` is an owned +value that, when we call the `join` method on it, will wait for its thread to +finish. Listing 16-2 shows how to use the `JoinHandle` of the thread we created +in Listing 16-1 and call `join` to make sure the spawned thread finishes before +`main` exits: + +Filename: src/main.rs + +``` +use std::thread; +use std::time::Duration; + +fn main() { + let handle = thread::spawn(|| { + for i in 1..10 { + println!("hi number {} from the spawned thread!", i); + thread::sleep(Duration::from_millis(1)); + } + }); + + for i in 1..5 { + println!("hi number {} from the main thread!", i); + thread::sleep(Duration::from_millis(1)); + } + + handle.join().unwrap(); +} +``` + +Listing 16-2: Saving a `JoinHandle` from `thread::spawn` to guarantee the +thread is run to completion + +Calling `join` on the handle blocks the thread currently running until the +thread represented by the handle terminates. *Blocking* a thread means that +thread is prevented from performing work or exiting. Because we’ve put the call +to `join` after the main thread’s `for` loop, running Listing 16-2 should +produce output similar to this: + +``` +hi number 1 from the main thread! +hi number 2 from the main thread! +hi number 1 from the spawned thread! +hi number 3 from the main thread! +hi number 2 from the spawned thread! +hi number 4 from the main thread! +hi number 3 from the spawned thread! +hi number 4 from the spawned thread! +hi number 5 from the spawned thread! +hi number 6 from the spawned thread! +hi number 7 from the spawned thread! +hi number 8 from the spawned thread! +hi number 9 from the spawned thread! +``` + +The two threads continue alternating, but the main thread waits because of the +call to `handle.join()` and does not end until the spawned thread is finished. + +But let’s see what happens when we instead move `handle.join()` before the +`for` loop in `main`, like this: + +Filename: src/main.rs + +``` +use std::thread; +use std::time::Duration; + +fn main() { + let handle = thread::spawn(|| { + for i in 1..10 { + println!("hi number {} from the spawned thread!", i); + thread::sleep(Duration::from_millis(1)); + } + }); + + handle.join().unwrap(); + + for i in 1..5 { + println!("hi number {} from the main thread!", i); + thread::sleep(Duration::from_millis(1)); + } +} +``` + +The main thread will wait for the spawned thread to finish and then run its +`for` loop, so the output won’t be interleaved anymore, as shown here: + +``` +hi number 1 from the spawned thread! +hi number 2 from the spawned thread! +hi number 3 from the spawned thread! +hi number 4 from the spawned thread! +hi number 5 from the spawned thread! +hi number 6 from the spawned thread! +hi number 7 from the spawned thread! +hi number 8 from the spawned thread! +hi number 9 from the spawned thread! +hi number 1 from the main thread! +hi number 2 from the main thread! +hi number 3 from the main thread! +hi number 4 from the main thread! +``` + +Small details, such as where `join` is called, can affect whether or not your +threads run at the same time. + +### Using `move` Closures with Threads + +The `move` keyword is often used with closures passed to `thread::spawn` +because the closure will then take ownership of the values it uses from the +environment, thus transferring ownership of those values from one thread to +another. In the “Capturing the Environment with Closures” section of Chapter 13, we discussed `move` in the context of closures. Now, +we’ll concentrate more on the interaction between `move` and `thread::spawn` + +Notice in Listing 16-1 that the closure we pass to `thread::spawn` takes no +arguments: we’re not using any data from the main thread in the spawned +thread’s code. To use data from the main thread in the spawned thread, the +spawned thread’s closure must capture the values it needs. Listing 16-3 shows +an attempt to create a vector in the main thread and use it in the spawned +thread. However, this won’t yet work, as you’ll see in a moment. + +Filename: src/main.rs + +``` +use std::thread; + +fn main() { + let v = vec![1, 2, 3]; + + let handle = thread::spawn(|| { + println!("Here's a vector: {:?}", v); + }); + + handle.join().unwrap(); +} +``` + +Listing 16-3: Attempting to use a vector created by the main thread in another +thread + +The closure uses `v`, so it will capture `v` and make it part of the closure’s +environment. Because `thread::spawn` runs this closure in a new thread, we +should be able to access `v` inside that new thread. But when we compile this +example, we get the following error: + +``` +error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function + --> src/main.rs:6:32 + | +6 | let handle = thread::spawn(|| { + | ^^ may outlive borrowed value `v` +7 | println!("Here's a vector: {:?}", v); + | - `v` is borrowed here + | +note: function requires argument type to outlive `'static` + --> src/main.rs:6:18 + | +6 | let handle = thread::spawn(|| { + | __________________^ +7 | | println!("Here's a vector: {:?}", v); +8 | | }); + | |______^ +help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword + | +6 | let handle = thread::spawn(move || { + | ++++ +``` + +Rust *infers* how to capture `v`, and because `println!` only needs a reference +to `v`, the closure tries to borrow `v`. However, there’s a problem: Rust can’t +tell how long the spawned thread will run, so it doesn’t know if the reference +to `v` will always be valid. + +Listing 16-4 provides a scenario that’s more likely to have a reference to `v` +that won’t be valid: + +Filename: src/main.rs + +``` +use std::thread; + +fn main() { + let v = vec![1, 2, 3]; + + let handle = thread::spawn(|| { + println!("Here's a vector: {:?}", v); + }); + + drop(v); // oh no! + + handle.join().unwrap(); +} +``` + +Listing 16-4: A thread with a closure that attempts to capture a reference to +`v` from a main thread that drops `v` + +If we were allowed to run this code, there’s a possibility the spawned thread +would be immediately put in the background without running at all. The spawned +thread has a reference to `v` inside, but the main thread immediately drops +`v`, using the `drop` function we discussed in Chapter 15. Then, when the +spawned thread starts to execute, `v` is no longer valid, so a reference to it +is also invalid. Oh no! + +To fix the compiler error in Listing 16-3, we can use the error message’s +advice: + +``` +help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword + | +6 | let handle = thread::spawn(move || { + | ++++ +``` + +By adding the `move` keyword before the closure, we force the closure to take +ownership of the values it’s using rather than allowing Rust to infer that it +should borrow the values. The modification to Listing 16-3 shown in Listing +16-5 will compile and run as we intend: + +Filename: src/main.rs + +``` +use std::thread; + +fn main() { + let v = vec![1, 2, 3]; + + let handle = thread::spawn(move || { + println!("Here's a vector: {:?}", v); + }); + + handle.join().unwrap(); +} +``` + +Listing 16-5: Using the `move` keyword to force a closure to take ownership of +the values it uses + +What would happen to the code in Listing 16-4 where the main thread called +`drop` if we use a `move` closure? Would `move` fix that case? Unfortunately, +no; we would get a different error because what Listing 16-4 is trying to do +isn’t allowed for a different reason. If we added `move` to the closure, we +would move `v` into the closure’s environment, and we could no longer call +`drop` on it in the main thread. We would get this compiler error instead: + +``` +error[E0382]: use of moved value: `v` + --> src/main.rs:10:10 + | +4 | let v = vec![1, 2, 3]; + | - move occurs because `v` has type `Vec`, which does not implement the `Copy` trait +5 | +6 | let handle = thread::spawn(move || { + | ------- value moved into closure here +7 | println!("Here's a vector: {:?}", v); + | - variable moved due to use in closure +... +10 | drop(v); // oh no! + | ^ value used here after move +``` + +Rust’s ownership rules have saved us again! We got an error from the code in +Listing 16-3 because Rust was being conservative and only borrowing `v` for the +thread, which meant the main thread could theoretically invalidate the spawned +thread’s reference. By telling Rust to move ownership of `v` to the spawned +thread, we’re guaranteeing Rust that the main thread won’t use `v` anymore. If +we change Listing 16-4 in the same way, we’re then violating the ownership +rules when we try to use `v` in the main thread. The `move` keyword overrides +Rust’s conservative default of borrowing; it doesn’t let us violate the +ownership rules. + +With a basic understanding of threads and the thread API, let’s look at what we +can *do* with threads. + +## Using Message Passing to Transfer Data Between Threads + +One increasingly popular approach to ensuring safe concurrency is *message +passing*, where threads or actors communicate by sending each other messages +containing data. Here’s the idea in a slogan from the Go language +documentation at *https://golang.org/doc/effective_go.html#concurrency*: +“Do not communicate by sharing memory; instead, share memory by communicating.” + +One major tool Rust has for accomplishing message-sending concurrency is the +*channel*, a programming concept that Rust’s standard library provides an +implementation of. You can imagine a channel in programming as being like a +channel of water, such as a stream or a river. If you put something like a +rubber duck or boat into a stream, it will travel downstream to the end of the +waterway. + +A channel in programming has two halves: a transmitter and a receiver. The +transmitter half is the upstream location where you put rubber ducks into the +river, and the receiver half is where the rubber duck ends up downstream. One +part of your code calls methods on the transmitter with the data you want to +send, and another part checks the receiving end for arriving messages. A +channel is said to be *closed* if either the transmitter or receiver half is +dropped. + +Here, we’ll work up to a program that has one thread to generate values and +send them down a channel, and another thread that will receive the values and +print them out. We’ll be sending simple values between threads using a channel +to illustrate the feature. Once you’re familiar with the technique, you could +use channels to implement a chat system or a system where many threads perform +parts of a calculation and send the parts to one thread that aggregates the +results. + +First, in Listing 16-6, we’ll create a channel but not do anything with it. +Note that this won’t compile yet because Rust can’t tell what type of values we +want to send over the channel. + +Filename: src/main.rs + +``` +use std::sync::mpsc; + +fn main() { + let (tx, rx) = mpsc::channel(); +} +``` + +Listing 16-6: Creating a channel and assigning the two halves to `tx` and `rx` + +We create a new channel using the `mpsc::channel` function; `mpsc` stands for +*multiple producer, single consumer*. In short, the way Rust’s standard library +implements channels means a channel can have multiple *sending* ends that +produce values but only one *receiving* end that consumes those values. Imagine +multiple streams flowing together into one big river: everything sent down any +of the streams will end up in one river at the end. We’ll start with a single +producer for now, but we’ll add multiple producers when we get this example +working. + +The `mpsc::channel` function returns a tuple, the first element of which is the +sending end and the second element is the receiving end. The abbreviations `tx` +and `rx` are traditionally used in many fields for *transmitter* and *receiver* +respectively, so we name our variables as such to indicate each end. We’re +using a `let` statement with a pattern that destructures the tuples; we’ll +discuss the use of patterns in `let` statements and destructuring in Chapter +18. Using a `let` statement this way is a convenient approach to extract the +pieces of the tuple returned by `mpsc::channel`. + +Let’s move the transmitting end into a spawned thread and have it send one +string so the spawned thread is communicating with the main thread, as shown in +Listing 16-7. This is like putting a rubber duck in the river upstream or +sending a chat message from one thread to another. + +Filename: src/main.rs + +``` +use std::sync::mpsc; +use std::thread; + +fn main() { + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + let val = String::from("hi"); + tx.send(val).unwrap(); + }); +} +``` + +Listing 16-7: Moving `tx` to a spawned thread and sending “hi” + +Again, we’re using `thread::spawn` to create a new thread and then using `move` +to move `tx` into the closure so the spawned thread owns `tx`. The spawned +thread needs to own the transmitting end of the channel to be able to send +messages through the channel. + +The transmitting end has a `send` method that takes the value we want to send. +The `send` method returns a `Result` type, so if the receiving end has +already been dropped and there’s nowhere to send a value, the send operation +will return an error. In this example, we’re calling `unwrap` to panic in case +of an error. But in a real application, we would handle it properly: return to +Chapter 9 to review strategies for proper error handling. + +In Listing 16-8, we’ll get the value from the receiving end of the channel in +the main thread. This is like retrieving the rubber duck from the water at the +end of the river or like getting a chat message. + +Filename: src/main.rs + +``` +use std::sync::mpsc; +use std::thread; + +fn main() { + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + let val = String::from("hi"); + tx.send(val).unwrap(); + }); + + let received = rx.recv().unwrap(); + println!("Got: {}", received); +} +``` + +Listing 16-8: Receiving the value “hi” in the main thread and printing it + +The receiving end of a channel has two useful methods: `recv` and `try_recv`. +We’re using `recv`, short for *receive*, which will block the main thread’s +execution and wait until a value is sent down the channel. Once a value is +sent, `recv` will return it in a `Result`. When the sending end of the +channel closes, `recv` will return an error to signal that no more values will +be coming. + +The `try_recv` method doesn’t block, but will instead return a `Result` +immediately: an `Ok` value holding a message if one is available and an `Err` +value if there aren’t any messages this time. Using `try_recv` is useful if +this thread has other work to do while waiting for messages: we could write a +loop that calls `try_recv` every so often, handles a message if one is +available, and otherwise does other work for a little while until checking +again. + +We’ve used `recv` in this example for simplicity; we don’t have any other work +for the main thread to do other than wait for messages, so blocking the main +thread is appropriate. + +When we run the code in Listing 16-8, we’ll see the value printed from the main +thread: + +``` +Got: hi +``` + +Perfect! + +### Channels and Ownership Transference + +The ownership rules play a vital role in message sending because they help you +write safe, concurrent code. Preventing errors in concurrent programming is the +advantage of thinking about ownership throughout your Rust programs. Let’s do +an experiment to show how channels and ownership work together to prevent +problems: we’ll try to use a `val` value in the spawned thread *after* we’ve +sent it down the channel. Try compiling the code in Listing 16-9 to see why +this code isn’t allowed: + +Filename: src/main.rs + +``` +use std::sync::mpsc; +use std::thread; + +fn main() { + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + let val = String::from("hi"); + tx.send(val).unwrap(); + println!("val is {}", val); + }); + + let received = rx.recv().unwrap(); + println!("Got: {}", received); +} +``` + +Listing 16-9: Attempting to use `val` after we’ve sent it down the channel + +Here, we try to print `val` after we’ve sent it down the channel via `tx.send`. +Allowing this would be a bad idea: once the value has been sent to another +thread, that thread could modify or drop it before we try to use the value +again. Potentially, the other thread’s modifications could cause errors or +unexpected results due to inconsistent or nonexistent data. However, Rust gives +us an error if we try to compile the code in Listing 16-9: + +``` +error[E0382]: borrow of moved value: `val` + --> src/main.rs:10:31 + | +8 | let val = String::from("hi"); + | --- move occurs because `val` has type `String`, which does not implement the `Copy` trait +9 | tx.send(val).unwrap(); + | --- value moved here +10 | println!("val is {}", val); + | ^^^ value borrowed here after move +``` + +Our concurrency mistake has caused a compile time error. The `send` function +takes ownership of its parameter, and when the value is moved, the receiver +takes ownership of it. This stops us from accidentally using the value again +after sending it; the ownership system checks that everything is okay. + +### Sending Multiple Values and Seeing the Receiver Waiting + +The code in Listing 16-8 compiled and ran, but it didn’t clearly show us that +two separate threads were talking to each other over the channel. In Listing +16-10 we’ve made some modifications that will prove the code in Listing 16-8 is +running concurrently: the spawned thread will now send multiple messages and +pause for a second between each message. + +Filename: src/main.rs + +``` +use std::sync::mpsc; +use std::thread; +use std::time::Duration; + +fn main() { + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + let vals = vec![ + String::from("hi"), + String::from("from"), + String::from("the"), + String::from("thread"), + ]; + + for val in vals { + tx.send(val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + + for received in rx { + println!("Got: {}", received); + } +} +``` + +Listing 16-10: Sending multiple messages and pausing between each + +This time, the spawned thread has a vector of strings that we want to send to +the main thread. We iterate over them, sending each individually, and pause +between each by calling the `thread::sleep` function with a `Duration` value of +1 second. + +In the main thread, we’re not calling the `recv` function explicitly anymore: +instead, we’re treating `rx` as an iterator. For each value received, we’re +printing it. When the channel is closed, iteration will end. + +When running the code in Listing 16-10, you should see the following output +with a 1-second pause in between each line: + +``` +Got: hi +Got: from +Got: the +Got: thread +``` + +Because we don’t have any code that pauses or delays in the `for` loop in the +main thread, we can tell that the main thread is waiting to receive values from +the spawned thread. + +### Creating Multiple Producers by Cloning the Transmitter + +Earlier we mentioned that `mpsc` was an acronym for *multiple producer, +single consumer*. Let’s put `mpsc` to use and expand the code in Listing 16-10 +to create multiple threads that all send values to the same receiver. We can do +so by cloning the transmitting half of the channel, as shown in Listing 16-11: + +Filename: src/main.rs + +``` + // --snip-- + + let (tx, rx) = mpsc::channel(); + + let tx1 = tx.clone(); + thread::spawn(move || { + let vals = vec![ + String::from("hi"), + String::from("from"), + String::from("the"), + String::from("thread"), + ]; + + for val in vals { + tx1.send(val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + + thread::spawn(move || { + let vals = vec![ + String::from("more"), + String::from("messages"), + String::from("for"), + String::from("you"), + ]; + + for val in vals { + tx.send(val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + + for received in rx { + println!("Got: {}", received); + } + + // --snip-- +``` + +Listing 16-11: Sending multiple messages from multiple producers + +This time, before we create the first spawned thread, we call `clone` on the +sending end of the channel. This will give us a new sending handle we can pass +to the first spawned thread. We pass the original sending end of the channel to +a second spawned thread. This gives us two threads, each sending different +messages to the receiving end of the channel. + +When you run the code, your output should look something like this: + +``` +Got: hi +Got: more +Got: from +Got: messages +Got: for +Got: the +Got: thread +Got: you +``` + +You might see the values in another order; it depends on your system. This is +what makes concurrency interesting as well as difficult. If you experiment with +`thread::sleep`, giving it various values in the different threads, each run +will be more nondeterministic and create different output each time. + +Now that we’ve looked at how channels work, let’s look at a different method of +concurrency. + +## Shared-State Concurrency + +Message passing is a fine way of handling concurrency, but it’s not the only +one. Consider this part of the slogan from the Go language documentation again: +“do not communicate by sharing memory.” + +What would communicating by sharing memory look like? In addition, why would +message-passing enthusiasts not use it and do the opposite instead? + +In a way, channels in any programming language are similar to single ownership, +because once you transfer a value down a channel, you should no longer use that +value. Shared memory concurrency is like multiple ownership: multiple threads +can access the same memory location at the same time. As you saw in Chapter 15, +where smart pointers made multiple ownership possible, multiple ownership can +add complexity because these different owners need managing. Rust’s type system +and ownership rules greatly assist in getting this management correct. For an +example, let’s look at mutexes, one of the more common concurrency primitives +for shared memory. + +### Using Mutexes to Allow Access to Data from One Thread at a Time + +*Mutex* is an abbreviation for *mutual exclusion*, as in, a mutex allows only +one thread to access some data at any given time. To access the data in a +mutex, a thread must first signal that it wants access by asking to acquire the +mutex’s *lock*. The lock is a data structure that is part of the mutex that +keeps track of who currently has exclusive access to the data. Therefore, the +mutex is described as *guarding* the data it holds via the locking system. + +Mutexes have a reputation for being difficult to use because you have to +remember two rules: + +* You must attempt to acquire the lock before using the data. +* When you’re done with the data that the mutex guards, you must unlock the + data so other threads can acquire the lock. + +For a real-world metaphor for a mutex, imagine a panel discussion at a +conference with only one microphone. Before a panelist can speak, they have to +ask or signal that they want to use the microphone. When they get the +microphone, they can talk for as long as they want to and then hand the +microphone to the next panelist who requests to speak. If a panelist forgets to +hand the microphone off when they’re finished with it, no one else is able to +speak. If management of the shared microphone goes wrong, the panel won’t work +as planned! + +Management of mutexes can be incredibly tricky to get right, which is why so +many people are enthusiastic about channels. However, thanks to Rust’s type +system and ownership rules, you can’t get locking and unlocking wrong. + +#### The API of `Mutex` + +As an example of how to use a mutex, let’s start by using a mutex in a +single-threaded context, as shown in Listing 16-12: + +Filename: src/main.rs + +``` +use std::sync::Mutex; + +fn main() { + [1] let m = Mutex::new(5); + + { + [2] let mut num = m.lock().unwrap(); + [3] *num = 6; + [4] } + + [5] println!("m = {:?}", m); +} +``` + +Listing 16-12: Exploring the API of `Mutex` in a single-threaded context for +simplicity + +As with many types, we create a `Mutex` using the associated function `new` +[1]. To access the data inside the mutex, we use the `lock` method to acquire +the lock [2]. This call will block the current thread so it can’t do any work +until it’s our turn to have the lock. + +The call to `lock` would fail if another thread holding the lock panicked. In +that case, no one would ever be able to get the lock, so we’ve chosen to +`unwrap` and have this thread panic if we’re in that situation. + +After we’ve acquired the lock, we can treat the return value, named `num` in +this case, as a mutable reference to the data inside [3]. The type system +ensures that we acquire a lock before using the value in `m`: `Mutex` is +not an `i32`, so we *must* acquire the lock to be able to use the `i32` value. +We can’t forget; the type system won’t let us access the inner `i32` otherwise. + +As you might suspect, `Mutex` is a smart pointer. More accurately, the call +to `lock` *returns* a smart pointer called `MutexGuard`, wrapped in a +`LockResult` that we handled with the call to `unwrap`. The `MutexGuard` smart +pointer implements `Deref` to point at our inner data; the smart pointer also +has a `Drop` implementation that releases the lock automatically when a +`MutexGuard` goes out of scope, which happens at the end of the inner scope +[4]. As a result, we don’t risk forgetting to release the lock and blocking the +mutex from being used by other threads because the lock release happens +automatically. + +After dropping the lock, we can print the mutex value and see that we were able +to change the inner `i32` to 6 [5]. + +#### Sharing a `Mutex` Between Multiple Threads + +Now, let’s try to share a value between multiple threads using `Mutex`. +We’ll spin up 10 threads and have them each increment a counter value by 1, so +the counter goes from 0 to 10. The next example in Listing 16-13 will have +a compiler error, and we’ll use that error to learn more about using +`Mutex` and how Rust helps us use it correctly. + +Filename: src/main.rs + +``` +use std::sync::Mutex; +use std::thread; + +fn main() { + [1] let counter = Mutex::new(0); + let mut handles = vec![]; + + [2] for _ in 0..10 { + [3] let handle = thread::spawn(move || { + [4] let mut num = counter.lock().unwrap(); + + [5] *num += 1; + }); + [6] handles.push(handle); + } + + for handle in handles { + [7] handle.join().unwrap(); + } + + [8] println!("Result: {}", *counter.lock().unwrap()); +} +``` + +Listing 16-13: Ten threads each increment a counter guarded by a `Mutex` + +We create a `counter` variable to hold an `i32` inside a `Mutex` [1], as we +did in Listing 16-12. Next, we create 10 threads by iterating over a range of +numbers [2]. We use `thread::spawn` and give all the threads the same closure, +one that moves the counter into the thread [3], acquires a lock on the +`Mutex` by calling the `lock` method [4], and then adds 1 to the value in +the mutex [5]. When a thread finishes running its closure, `num` will go out of +scope and release the lock so another thread can acquire it. + +In the main thread, we collect all the join handles [6]. Then, as we did in +Listing 16-2, we call `join` on each handle to make sure all the threads finish +[7]. At that point, the main thread will acquire the lock and print the result +of this program [8]. + +We hinted that this example wouldn’t compile. Now let’s find out why! + +``` +error[E0382]: use of moved value: `counter` + --> src/main.rs:9:36 + | +5 | let counter = Mutex::new(0); + | ------- move occurs because `counter` has type `Mutex`, which does not implement the `Copy` trait +... +9 | let handle = thread::spawn(move || { + | ^^^^^^^ value moved into closure here, in previous iteration of loop +10 | let mut num = counter.lock().unwrap(); + | ------- use occurs due to use in closure +``` + +The error message states that the `counter` value was moved in the previous +iteration of the loop. So Rust is telling us that we can’t move the ownership +of lock `counter` into multiple threads. Let’s fix the compiler error with a +multiple-ownership method we discussed in Chapter 15. + +#### Multiple Ownership with Multiple Threads + +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. + +Filename: src/main.rs + +``` +use std::rc::Rc; +use std::sync::Mutex; +use std::thread; + +fn main() { + let counter = Rc::new(Mutex::new(0)); + let mut handles = vec![]; + + for _ in 0..10 { + let counter = Rc::clone(&counter); + let handle = thread::spawn(move || { + let mut num = counter.lock().unwrap(); + + *num += 1; + }); + handles.push(handle); + } + + for handle in handles { + handle.join().unwrap(); + } + + println!("Result: {}", *counter.lock().unwrap()); +} +``` + +Listing 16-14: Attempting to use `Rc` to allow multiple threads to own the +`Mutex` + +Once again, we compile and get... different errors! The compiler is teaching us +a lot. + +``` +[1] error[E0277]: `Rc>` cannot be sent between threads safely + --> src/main.rs:11:22 + | +11 | let handle = thread::spawn(move || { + | ______________________^^^^^^^^^^^^^_- + | | | + | | `Rc>` cannot be sent between threads safely +12 | | let mut num = counter.lock().unwrap(); +13 | | +14 | | *num += 1; +15 | | }); + | |_________- within this `[closure@src/main.rs:11:36: 15:10]` + | +[2] = help: within `[closure@src/main.rs:11:36: 15:10]`, the trait `Send` is not implemented for `Rc>` + = note: required because it appears within the type `[closure@src/main.rs:11:36: 15:10]` +note: required by a bound in `spawn` +``` + +Wow, that error message is very wordy! Here’s the important part to focus on: +`` `Rc>` cannot be sent between threads safely `` [1]. The compiler +is also telling us the reason why: ``the trait `Send` is not implemented for +`Rc>` `` [2]. We’ll talk about `Send` in the next section: it’s one +of the traits that ensures the types we use with threads are meant for use in +concurrent situations. + +Unfortunately, `Rc` is not safe to share across threads. When `Rc` +manages the reference count, it adds to the count for each call to `clone` and +subtracts from the count when each clone is dropped. But it doesn’t use any +concurrency primitives to make sure that changes to the count can’t be +interrupted by another thread. This could lead to wrong counts—subtle bugs that +could in turn lead to memory leaks or a value being dropped before we’re done +with it. What we need is a type exactly like `Rc` but one that makes changes +to the reference count in a thread-safe way. + +#### Atomic Reference Counting with `Arc` + +Fortunately, `Arc` *is* a type like `Rc` that is safe to use in +concurrent situations. The *a* stands for *atomic*, meaning it’s an *atomically +reference counted* type. Atomics are an additional kind of concurrency +primitive that we won’t cover in detail here: see the standard library +documentation for `std::sync::atomic` for more details. At this point, you just +need to know that atomics work like primitive types but are safe to share +across threads. + +You might then wonder why all primitive types aren’t atomic and why standard +library types aren’t implemented to use `Arc` by default. The reason is that +thread safety comes with a performance penalty that you only want to pay when +you really need to. If you’re just performing operations on values within a +single thread, your code can run faster if it doesn’t have to enforce the +guarantees atomics provide. + +Let’s return to our example: `Arc` and `Rc` have the same API, so we fix +our program by changing the `use` line, the call to `new`, and the call to +`clone`. The code in Listing 16-15 will finally compile and run: + +Filename: src/main.rs + +``` +use std::sync::{Arc, Mutex}; +use std::thread; + +fn main() { + let counter = Arc::new(Mutex::new(0)); + let mut handles = vec![]; + + for _ in 0..10 { + let counter = Arc::clone(&counter); + let handle = thread::spawn(move || { + let mut num = counter.lock().unwrap(); + + *num += 1; + }); + handles.push(handle); + } + + for handle in handles { + handle.join().unwrap(); + } + + println!("Result: {}", *counter.lock().unwrap()); +} +``` + +Listing 16-15: Using an `Arc` to wrap the `Mutex` to be able to share +ownership across multiple threads + +This code will print the following: + +``` +Result: 10 +``` + +We did it! We counted from 0 to 10, which may not seem very impressive, but it +did teach us a lot about `Mutex` and thread safety. You could also use this +program’s structure to do more complicated operations than just incrementing a +counter. Using this strategy, you can divide a calculation into independent +parts, split those parts across threads, and then use a `Mutex` to have each +thread update the final result with its part. + +### Similarities Between `RefCell`/`Rc` and `Mutex`/`Arc` + +You might have noticed that `counter` is immutable but we could get a mutable +reference to the value inside it; this means `Mutex` provides interior +mutability, as the `Cell` family does. In the same way we used `RefCell` in +Chapter 15 to allow us to mutate contents inside an `Rc`, we use `Mutex` +to mutate contents inside an `Arc`. + +Another detail to note is that Rust can’t protect you from all kinds of logic +errors when you use `Mutex`. Recall in Chapter 15 that using `Rc` came +with the risk of creating reference cycles, where two `Rc` values refer to +each other, causing memory leaks. Similarly, `Mutex` comes with the risk of +creating *deadlocks*. These occur when an operation needs to lock two resources +and two threads have each acquired one of the locks, causing them to wait for +each other forever. If you’re interested in deadlocks, try creating a Rust +program that has a deadlock; then research deadlock mitigation strategies for +mutexes in any language and have a go at implementing them in Rust. The +standard library API documentation for `Mutex` and `MutexGuard` offers +useful information. + +We’ll round out this chapter by talking about the `Send` and `Sync` traits and +how we can use them with custom types. + +## Extensible Concurrency with the `Sync` and `Send` Traits + +Interestingly, the Rust language has *very* few concurrency features. Almost +every concurrency feature we’ve talked about so far in this chapter has been +part of the standard library, not the language. Your options for handling +concurrency are not limited to the language or the standard library; you can +write your own concurrency features or use those written by others. + +However, two concurrency concepts are embedded in the language: the +`std::marker` traits `Sync` and `Send`. + +### Allowing Transference of Ownership Between Threads with `Send` + +The `Send` marker trait indicates that ownership of values of the type implementing +`Send` can be transferred between threads. Almost every Rust type is `Send`, +but there are some exceptions, including `Rc`: this cannot be `Send` because +if you cloned an `Rc` value and tried to transfer ownership of the clone to +another thread, both threads might update the reference count at the same time. +For this reason, `Rc` is implemented for use in single-threaded situations +where you don’t want to pay the thread-safe performance penalty. + +Therefore, Rust’s type system and trait bounds ensure that you can never +accidentally send an `Rc` value across threads unsafely. When we tried to do +this in Listing 16-14, we got the error `the trait Send is not implemented for +Rc>`. When we switched to `Arc`, which is `Send`, the code +compiled. + +Any type composed entirely of `Send` types is automatically marked as `Send` as +well. Almost all primitive types are `Send`, aside from raw pointers, which +we’ll discuss in Chapter 19. + +### Allowing Access from Multiple Threads with `Sync` + +The `Sync` marker trait indicates that it is safe for the type implementing +`Sync` to be referenced from multiple threads. In other words, any type `T` is +`Sync` if `&T` (an immutable reference to `T`) is `Send`, meaning the reference +can be sent safely to another thread. Similar to `Send`, primitive types are +`Sync`, and types composed entirely of types that are `Sync` are also `Sync`. + +The smart pointer `Rc` is also not `Sync` for the same reasons that it’s not +`Send`. The `RefCell` type (which we talked about in Chapter 15) and the +family of related `Cell` types are not `Sync`. The implementation of borrow +checking that `RefCell` does at runtime is not thread-safe. The smart +pointer `Mutex` is `Sync` and can be used to share access with multiple +threads as you saw in the “Sharing a `Mutex` Between Multiple +Threads” section. + +### Implementing `Send` and `Sync` Manually Is Unsafe + +Because types that are made up of `Send` and `Sync` traits are automatically +also `Send` and `Sync`, we don’t have to implement those traits manually. As +marker traits, they don’t even have any methods to implement. They’re just +useful for enforcing invariants related to concurrency. + +Manually implementing these traits involves implementing unsafe Rust code. +We’ll talk about using unsafe Rust code in Chapter 19; for now, the important +information is that building new concurrent types not made up of `Send` and +`Sync` parts requires careful thought to uphold the safety guarantees. “The +Rustonomicon” at *../nomicon/index.html* has more information about these guarantees and how to +uphold them. + +## Summary + +This isn’t the last you’ll see of concurrency in this book: the project in +Chapter 20 will use the concepts in this chapter in a more realistic situation +than the smaller examples discussed here. + +As mentioned earlier, because very little of how Rust handles concurrency is +part of the language, many concurrency solutions are implemented as crates. +These evolve more quickly than the standard library, so be sure to search +online for the current, state-of-the-art crates to use in multithreaded +situations. + +The Rust standard library provides channels for message passing and smart +pointer types, such as `Mutex` and `Arc`, that are safe to use in +concurrent contexts. The type system and the borrow checker ensure that the +code using these solutions won’t end up with data races or invalid references. +Once you get your code to compile, you can rest assured that it will happily +run on multiple threads without the kinds of hard-to-track-down bugs common in +other languages. Concurrent programming is no longer a concept to be afraid of: +go forth and make your programs concurrent, fearlessly! + +Next, we’ll talk about idiomatic ways to model problems and structure solutions +as your Rust programs get bigger. In addition, we’ll discuss how Rust’s idioms +relate to those you might be familiar with from object-oriented programming. diff --git a/src/doc/book/nostarch/chapter17.md b/src/doc/book/nostarch/chapter17.md new file mode 100644 index 0000000000..c56b8a2ccf --- /dev/null +++ b/src/doc/book/nostarch/chapter17.md @@ -0,0 +1,1208 @@ + + +[TOC] + +# Object Oriented Programming Features of Rust + +Object-oriented programming (OOP) is a way of modeling programs. Objects came +from Simula in the 1960s. Those objects influenced Alan Kay’s programming +architecture in which objects pass messages to each other. He coined the term +*object-oriented programming* in 1967 to describe this architecture. Many +competing definitions describe what OOP is; some definitions would classify +Rust as object oriented, but other definitions would not. In this chapter, +we’ll explore certain characteristics that are commonly considered object +oriented and how those characteristics translate to idiomatic Rust. We’ll then +show you how to implement an object-oriented design pattern in Rust and discuss +the trade-offs of doing so versus implementing a solution using some of Rust’s +strengths instead. + +## Characteristics of Object-Oriented Languages + +There is no consensus in the programming community about what features a +language must have to be considered object oriented. Rust is influenced by many +programming paradigms, including OOP; for example, we explored the features +that came from functional programming in Chapter 13. Arguably, OOP languages +share certain common characteristics, namely objects, encapsulation, and +inheritance. Let’s look at what each of those characteristics means and whether +Rust supports it. + +### Objects Contain Data and Behavior + +The book *Design Patterns: Elements of Reusable Object-Oriented Software* by +Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley +Professional, 1994), colloquially referred to as *The Gang of Four* book, is a +catalog of object-oriented design patterns. It defines OOP this way: + +> Object-oriented programs are made up of objects. An *object* packages both +> data and the procedures that operate on that data. The procedures are +> typically called *methods* or *operations*. + +Using this definition, Rust is object oriented: structs and enums have data, +and `impl` blocks provide methods on structs and enums. Even though structs and +enums with methods aren’t *called* objects, they provide the same +functionality, according to the Gang of Four’s definition of objects. + +### Encapsulation that Hides Implementation Details + +Another aspect commonly associated with OOP is the idea of *encapsulation*, +which means that the implementation details of an object aren’t accessible to +code using that object. Therefore, the only way to interact with an object is +through its public API; code using the object shouldn’t be able to reach into +the object’s internals and change data or behavior directly. This enables the +programmer to change and refactor an object’s internals without needing to +change the code that uses the object. + +We discussed how to control encapsulation in Chapter 7: we can use the `pub` +keyword to decide which modules, types, functions, and methods in our code +should be public, and by default everything else is private. For example, we +can define a struct `AveragedCollection` that has a field containing a vector +of `i32` values. The struct can also have a field that contains the average of +the values in the vector, meaning the average doesn’t have to be computed +on demand whenever anyone needs it. In other words, `AveragedCollection` will +cache the calculated average for us. Listing 17-1 has the definition of the +`AveragedCollection` struct: + +Filename: src/lib.rs + +``` +pub struct AveragedCollection { + list: Vec, + average: f64, +} +``` + +Listing 17-1: An `AveragedCollection` struct that maintains a list of integers +and the average of the items in the collection + +The struct is marked `pub` so that other code can use it, but the fields within +the struct remain private. This is important in this case because we want to +ensure that whenever a value is added or removed from the list, the average is +also updated. We do this by implementing `add`, `remove`, and `average` methods +on the struct, as shown in Listing 17-2: + +Filename: src/lib.rs + +``` +impl AveragedCollection { + pub fn add(&mut self, value: i32) { + self.list.push(value); + self.update_average(); + } + + pub fn remove(&mut self) -> Option { + let result = self.list.pop(); + match result { + Some(value) => { + self.update_average(); + Some(value) + } + None => None, + } + } + + pub fn average(&self) -> f64 { + self.average + } + + fn update_average(&mut self) { + let total: i32 = self.list.iter().sum(); + self.average = total as f64 / self.list.len() as f64; + } +} +``` + +Listing 17-2: Implementations of the public methods `add`, `remove`, and +`average` on `AveragedCollection` + +The public methods `add`, `remove`, and `average` are the only ways to access +or modify data in an instance of `AveragedCollection`. When an item is added +to `list` using the `add` method or removed using the `remove` method, the +implementations of each call the private `update_average` method that handles +updating the `average` field as well. + +We leave the `list` and `average` fields private so there is no way for +external code to add or remove items to the `list` field directly; otherwise, +the `average` field might become out of sync when the `list` changes. The +`average` method returns the value in the `average` field, allowing external +code to read the `average` but not modify it. + +Because we’ve encapsulated the implementation details of the struct +`AveragedCollection`, we can easily change aspects, such as the data structure, +in the future. For instance, we could use a `HashSet` instead of a +`Vec` for the `list` field. As long as the signatures of the `add`, +`remove`, and `average` public methods stay the same, code using +`AveragedCollection` wouldn’t need to change. If we made `list` public instead, +this wouldn’t necessarily be the case: `HashSet` and `Vec` have +different methods for adding and removing items, so the external code would +likely have to change if it were modifying `list` directly. + +If encapsulation is a required aspect for a language to be considered object +oriented, then Rust meets that requirement. The option to use `pub` or not for +different parts of code enables encapsulation of implementation details. + +### Inheritance as a Type System and as Code Sharing + +*Inheritance* is a mechanism whereby an object can inherit from another +object’s definition, thus gaining the parent object’s data and behavior without +you having to define them again. + +If a language must have inheritance to be an object-oriented language, then +Rust is not one. There is no way to define a struct that inherits the parent +struct’s fields and method implementations. However, if you’re used to having +inheritance in your programming toolbox, you can use other solutions in Rust, +depending on your reason for reaching for inheritance in the first place. + +You choose inheritance for two main reasons. One is for reuse of code: you can +implement particular behavior for one type, and inheritance enables you to +reuse that implementation for a different type. You can share Rust code using +default trait method implementations instead, which you saw in Listing 10-14 +when we added a default implementation of the `summarize` method on the +`Summary` trait. Any type implementing the `Summary` trait would have the +`summarize` method available on it without any further code. This is similar to +a parent class having an implementation of a method and an inheriting child +class also having the implementation of the method. We can also override the +default implementation of the `summarize` method when we implement the +`Summary` trait, which is similar to a child class overriding the +implementation of a method inherited from a parent class. + +The other reason to use inheritance relates to the type system: to enable a +child type to be used in the same places as the parent type. This is also +called *polymorphism*, which means that you can substitute multiple objects for +each other at runtime if they share certain characteristics. + +> ### Polymorphism +> +> To many people, polymorphism is synonymous with inheritance. But it’s +> actually a more general concept that refers to code that can work with data +> of multiple types. For inheritance, those types are generally subclasses. +> +> Rust instead uses generics to abstract over different possible types and +> trait bounds to impose constraints on what those types must provide. This is +> sometimes called *bounded parametric polymorphism*. + +Inheritance has recently fallen out of favor as a programming design solution +in many programming languages because it’s often at risk of sharing more code +than necessary. Subclasses shouldn’t always share all characteristics of their +parent class but will do so with inheritance. This can make a program’s design +less flexible. It also introduces the possibility of calling methods on +subclasses that don’t make sense or that cause errors because the methods don’t +apply to the subclass. In addition, some languages will only allow a subclass +to inherit from one class, further restricting the flexibility of a program’s +design. + +For these reasons, Rust takes a different approach, using trait objects instead +of inheritance. Let’s look at how trait objects enable polymorphism in Rust. + +## Using Trait Objects That Allow for Values of Different Types + +In Chapter 8, we mentioned that one limitation of vectors is that they can +store elements of only one type. We created a workaround in Listing 8-10 where +we defined a `SpreadsheetCell` enum that had variants to hold integers, floats, +and text. This meant we could store different types of data in each cell and +still have a vector that represented a row of cells. This is a perfectly good +solution when our interchangeable items are a fixed set of types that we know +when our code is compiled. + +However, sometimes we want our library user to be able to extend the set of +types that are valid in a particular situation. To show how we might achieve +this, we’ll create an example graphical user interface (GUI) tool that iterates +through a list of items, calling a `draw` method on each one to draw it to the +screen—a common technique for GUI tools. We’ll create a library crate called +`gui` that contains the structure of a GUI library. This crate might include +some types for people to use, such as `Button` or `TextField`. In addition, +`gui` users will want to create their own types that can be drawn: for +instance, one programmer might add an `Image` and another might add a +`SelectBox`. + +We won’t implement a fully fledged GUI library for this example but will show +how the pieces would fit together. At the time of writing the library, we can’t +know and define all the types other programmers might want to create. But we do +know that `gui` needs to keep track of many values of different types, and it +needs to call a `draw` method on each of these differently typed values. It +doesn’t need to know exactly what will happen when we call the `draw` method, +just that the value will have that method available for us to call. + +To do this in a language with inheritance, we might define a class named +`Component` that has a method named `draw` on it. The other classes, such as +`Button`, `Image`, and `SelectBox`, would inherit from `Component` and thus +inherit the `draw` method. They could each override the `draw` method to define +their custom behavior, but the framework could treat all of the types as if +they were `Component` instances and call `draw` on them. But because Rust +doesn’t have inheritance, we need another way to structure the `gui` library to +allow users to extend it with new types. + +### Defining a Trait for Common Behavior + +To implement the behavior we want `gui` to have, we’ll define a trait named +`Draw` that will have one method named `draw`. Then we can define a vector that +takes a *trait object*. A trait object points to both an instance of a type +implementing our specified trait as well as a table used to look up trait +methods on that type at runtime. We create a trait object by specifying some +sort of pointer, such as a `&` reference or a `Box` smart pointer, then the +`dyn` keyword, and then specifying the relevant trait. (We’ll talk about the +reason trait objects must use a pointer in Chapter 19 in the section +“Dynamically Sized Types and the `Sized` Trait.”) We can use trait objects in place of a generic or concrete type. +Wherever we use a trait object, Rust’s type system will ensure at compile time +that any value used in that context will implement the trait object’s trait. +Consequently, we don’t need to know all the possible types at compile time. + +We’ve mentioned that in Rust, we refrain from calling structs and enums +“objects” to distinguish them from other languages’ objects. In a struct or +enum, the data in the struct fields and the behavior in `impl` blocks are +separated, whereas in other languages, the data and behavior combined into one +concept is often labeled an object. However, trait objects *are* more like +objects in other languages in the sense that they combine data and behavior. +But trait objects differ from traditional objects in that we can’t add data to +a trait object. Trait objects aren’t as generally useful as objects in other +languages: their specific purpose is to allow abstraction across common +behavior. + +Listing 17-3 shows how to define a trait named `Draw` with one method named +`draw`: + +Filename: src/lib.rs + +``` +pub trait Draw { + fn draw(&self); +} +``` + +Listing 17-3: Definition of the `Draw` trait + +This syntax should look familiar from our discussions on how to define traits +in Chapter 10. Next comes some new syntax: Listing 17-4 defines a struct named +`Screen` that holds a vector named `components`. This vector is of type +`Box`, which is a trait object; it’s a stand-in for any type inside +a `Box` that implements the `Draw` trait. + +Filename: src/lib.rs + +``` +pub struct Screen { + pub components: Vec>, +} +``` + +Listing 17-4: Definition of the `Screen` struct with a `components` field +holding a vector of trait objects that implement the `Draw` trait + +On the `Screen` struct, we’ll define a method named `run` that will call the +`draw` method on each of its `components`, as shown in Listing 17-5: + +Filename: src/lib.rs + +``` +impl Screen { + pub fn run(&self) { + for component in self.components.iter() { + component.draw(); + } + } +} +``` + +Listing 17-5: A `run` method on `Screen` that calls the `draw` method on each +component + +This works differently from defining a struct that uses a generic type +parameter with trait bounds. A generic type parameter can only be substituted +with one concrete type at a time, whereas trait objects allow for multiple +concrete types to fill in for the trait object at runtime. For example, we +could have defined the `Screen` struct using a generic type and a trait bound +as in Listing 17-6: + +Filename: src/lib.rs + +``` +pub struct Screen { + pub components: Vec, +} + +impl Screen +where + T: Draw, +{ + pub fn run(&self) { + for component in self.components.iter() { + component.draw(); + } + } +} +``` + +Listing 17-6: An alternate implementation of the `Screen` struct and its `run` +method using generics and trait bounds + +This restricts us to a `Screen` instance that has a list of components all of +type `Button` or all of type `TextField`. If you’ll only ever have homogeneous +collections, using generics and trait bounds is preferable because the +definitions will be monomorphized at compile time to use the concrete types. + +On the other hand, with the method using trait objects, one `Screen` instance +can hold a `Vec` that contains a `Box"; } - function showResults(results, go_to_first) { + function showResults(results, go_to_first, filterCrates) { var search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true" @@ -1126,9 +1156,19 @@ window.initSearch = function(rawSearchIndex) { } } - var output = "

Results for " + escape(query.query) + + let crates = ""; + if (window.ALL_CRATES.length > 1) { + crates = ` in `; + } + var output = `
+

Results for ${escape(query.query)} ` + (query.type ? " (type: " + escape(query.type) + ")" : "") + "

" + - "
" + + crates + + `
` + makeTabHeader(0, "In Names", ret_others[1]) + makeTabHeader(1, "In Parameters", ret_in_args[1]) + makeTabHeader(2, "In Return Types", ret_returned[1]) + @@ -1141,6 +1181,10 @@ window.initSearch = function(rawSearchIndex) { resultsElem.appendChild(ret_returned[0]); search.innerHTML = output; + let crateSearch = document.getElementById("crate-search"); + if (crateSearch) { + crateSearch.addEventListener("input", updateCrate); + } search.appendChild(resultsElem); // Reset focused elements. searchState.focusedByTab = [null, null, null]; @@ -1153,110 +1197,19 @@ window.initSearch = function(rawSearchIndex) { } function execSearch(query, searchWords, filterCrates) { - function getSmallest(arrays, positions, notDuplicates) { - var start = null; - - for (var it = 0, len = positions.length; it < len; ++it) { - if (arrays[it].length > positions[it] && - (start === null || start > arrays[it][positions[it]].lev) && - !notDuplicates[arrays[it][positions[it]].fullPath]) { - start = arrays[it][positions[it]].lev; - } - } - return start; - } - - function mergeArrays(arrays) { - var ret = []; - var positions = []; - var notDuplicates = {}; - - for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) { - positions.push(0); - } - while (ret.length < MAX_RESULTS) { - var smallest = getSmallest(arrays, positions, notDuplicates); - - if (smallest === null) { - break; - } - for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) { - if (arrays[x].length > positions[x] && - arrays[x][positions[x]].lev === smallest && - !notDuplicates[arrays[x][positions[x]].fullPath]) { - ret.push(arrays[x][positions[x]]); - notDuplicates[arrays[x][positions[x]].fullPath] = true; - positions[x] += 1; - } - } - } - return ret; - } - - // Split search query by ",", while respecting angle bracket nesting. - // Since "<" is an alias for the Ord family of traits, it also uses - // lookahead to distinguish "<"-as-less-than from "<"-as-angle-bracket. - // - // tokenizeQuery("A, D") == ["A", "D"] - // tokenizeQuery("A)/g; - var ret = []; - var start = 0; - for (i = 0; i < l; ++i) { - switch (raw[i]) { - case "<": - nextAngle.lastIndex = i + 1; - matched = nextAngle.exec(raw); - if (matched && matched[1] === '>') { - depth += 1; - } - break; - case ">": - if (depth > 0) { - depth -= 1; - } - break; - case ",": - if (depth === 0) { - ret.push(raw.substring(start, i)); - start = i + 1; - } - break; - } - } - if (start !== i) { - ret.push(raw.substring(start, i)); - } - return ret; - } - - var queries = tokenizeQuery(query.raw); + query = query.raw.trim(); var results = { "in_args": [], "returned": [], "others": [], }; - for (var i = 0, len = queries.length; i < len; ++i) { - query = queries[i].trim(); - if (query.length !== 0) { - var tmp = execQuery(getQuery(query), searchWords, filterCrates); + if (query.length !== 0) { + var tmp = execQuery(getQuery(query), searchWords, filterCrates); - results.in_args.push(tmp.in_args); - results.returned.push(tmp.returned); - results.others.push(tmp.others); - } - } - if (queries.length > 1) { - return { - "in_args": mergeArrays(results.in_args), - "returned": mergeArrays(results.returned), - "others": mergeArrays(results.others), - }; + results.in_args.push(tmp.in_args); + results.returned.push(tmp.returned); + results.others.push(tmp.others); } return { "in_args": results.in_args[0], @@ -1265,17 +1218,6 @@ window.initSearch = function(rawSearchIndex) { }; } - function getFilterCrates() { - var elem = document.getElementById("crate-search"); - - if (elem && elem.value !== "All crates" && - hasOwnPropertyRustdoc(rawSearchIndex, elem.value)) - { - return elem.value; - } - return undefined; - } - /** * Perform a search based on the current state of the search input element * and display the results. @@ -1295,28 +1237,36 @@ window.initSearch = function(rawSearchIndex) { } if (!forced && query.id === currentResults) { if (query.query.length > 0) { - searchState.putBackSearch(searchState.input); + putBackSearch(); } return; } + var filterCrates = getFilterCrates(); + + // In case we have no information about the saved crate and there is a URL query parameter, + // we override it with the URL query parameter. + if (filterCrates === null && params["filter-crate"] !== undefined) { + filterCrates = params["filter-crate"]; + } + // Update document title to maintain a meaningful browser history searchState.title = "Results for " + query.query + " - Rust"; // Because searching is incremental by character, only the most // recent search query is added to the browser history. if (searchState.browserSupportsHistoryApi()) { - var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) + - window.location.hash; + var newURL = buildUrl(query.raw, filterCrates); + if (!history.state && !params.search) { - history.pushState(query, "", newURL); + history.pushState(null, "", newURL); } else { - history.replaceState(query, "", newURL); + history.replaceState(null, "", newURL); } } - var filterCrates = getFilterCrates(); - showResults(execSearch(query, searchWords, filterCrates), params["go_to_first"]); + showResults(execSearch(query, searchWords, filterCrates), + params["go_to_first"], filterCrates); } function buildIndex(rawSearchIndex) { @@ -1480,12 +1430,28 @@ window.initSearch = function(rawSearchIndex) { search(); } + function putBackSearch() { + var search_input = searchState.input; + if (!searchState.input) { + return; + } + var search = searchState.outputElement(); + if (search_input.value !== "" && hasClass(search, "hidden")) { + searchState.showResults(search); + if (searchState.browserSupportsHistoryApi()) { + history.replaceState(null, "", + buildUrl(search_input.value, getFilterCrates())); + } + document.title = searchState.title; + } + } + function registerSearchEvents() { var searchAfter500ms = function() { searchState.clearInputTimeout(); if (searchState.input.value.length === 0) { if (searchState.browserSupportsHistoryApi()) { - history.replaceState("", window.currentCrate + " - Rust", + history.replaceState(null, window.currentCrate + " - Rust", getNakedUrl() + window.location.hash); } searchState.hideResults(); @@ -1552,18 +1518,13 @@ window.initSearch = function(rawSearchIndex) { } }); + searchState.input.addEventListener("focus", function() { + putBackSearch(); + }); - var selectCrate = document.getElementById("crate-search"); - if (selectCrate) { - selectCrate.onchange = function() { - updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value); - // In case you "cut" the entry from the search input, then change the crate filter - // before paste back the previous search, you get the old search results without - // the filter. To prevent this, we need to remove the previous results. - currentResults = null; - search(undefined, true); - }; - } + searchState.input.addEventListener("blur", function() { + searchState.input.placeholder = searchState.input.origPlaceholder; + }); // Push and pop states are used to add search results to the browser // history. @@ -1616,12 +1577,35 @@ window.initSearch = function(rawSearchIndex) { }; } + function updateCrate(ev) { + if (ev.target.value === "All crates") { + // If we don't remove it from the URL, it'll be picked up again by the search. + var params = searchState.getQueryStringParams(); + var query = searchState.input.value.trim(); + if (!history.state && !params.search) { + history.pushState(null, "", buildUrl(query, null)); + } else { + history.replaceState(null, "", buildUrl(query, null)); + } + } + // In case you "cut" the entry from the search input, then change the crate filter + // before paste back the previous search, you get the old search results without + // the filter. To prevent this, we need to remove the previous results. + currentResults = null; + search(undefined, true); + } + searchWords = buildIndex(rawSearchIndex); registerSearchEvents(); - // If there's a search term in the URL, execute the search now. - if (searchState.getQueryStringParams().search) { - search(); + + function runSearchIfNeeded() { + // If there's a search term in the URL, execute the search now. + if (searchState.getQueryStringParams().search) { + search(); + } } + + runSearchIfNeeded(); }; if (window.searchIndex !== undefined) { diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 4f10e14e85..139fa5c9a1 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -1,15 +1,18 @@ // Local js definitions: /* global getSettingValue, getVirtualKey, onEachLazy, updateLocalStorage, updateSystemTheme */ +/* global addClass, removeClass */ (function () { function changeSetting(settingName, value) { - updateLocalStorage("rustdoc-" + settingName, value); + updateLocalStorage(settingName, value); switch (settingName) { + case "theme": case "preferred-dark-theme": case "preferred-light-theme": case "use-system-theme": updateSystemTheme(); + updateLightAndDark(); break; } } @@ -29,7 +32,28 @@ } } + function showLightAndDark() { + addClass(document.getElementById("theme").parentElement, "hidden"); + removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden"); + removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden"); + } + + function hideLightAndDark() { + addClass(document.getElementById("preferred-light-theme").parentElement, "hidden"); + addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden"); + removeClass(document.getElementById("theme").parentElement, "hidden"); + } + + function updateLightAndDark() { + if (getSettingValue("use-system-theme") !== "false") { + showLightAndDark(); + } else { + hideLightAndDark(); + } + } + function setEvents() { + updateLightAndDark(); onEachLazy(document.getElementsByClassName("slider"), function(elem) { var toggle = elem.previousElementSibling; var settingId = toggle.id; @@ -54,6 +78,19 @@ changeSetting(this.id, this.value); }; }); + onEachLazy(document.querySelectorAll("input[type=\"radio\"]"), function(elem) { + const settingId = elem.name; + const settingValue = getSettingValue(settingId); + if (settingValue !== null && settingValue !== "null") { + elem.checked = settingValue === elem.value; + } + elem.addEventListener("change", function(ev) { + changeSetting(ev.target.name, ev.target.value); + }); + }); + document.getElementById("back").addEventListener("click", function() { + history.back(); + }); } window.addEventListener("DOMContentLoaded", setEvents); diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 81dc0b2fb1..90490acccf 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -82,11 +82,11 @@ function toggleSidebar() { if (child.innerText === ">") { sidebar.classList.add("expanded"); child.innerText = "<"; - updateLocalStorage("rustdoc-source-sidebar-show", "true"); + updateLocalStorage("source-sidebar-show", "true"); } else { sidebar.classList.remove("expanded"); child.innerText = ">"; - updateLocalStorage("rustdoc-source-sidebar-show", "false"); + updateLocalStorage("source-sidebar-show", "false"); } } @@ -97,7 +97,7 @@ function createSidebarToggle() { var inner = document.createElement("div"); - if (getCurrentValue("rustdoc-source-sidebar-show") === "true") { + if (getCurrentValue("source-sidebar-show") === "true") { inner.innerText = "<"; } else { inner.innerText = ">"; @@ -120,7 +120,7 @@ function createSourceSidebar() { var sidebar = document.createElement("div"); sidebar.id = "source-sidebar"; - if (getCurrentValue("rustdoc-source-sidebar-show") !== "true") { + if (getCurrentValue("source-sidebar-show") !== "true") { container.classList.remove("expanded"); } else { container.classList.add("expanded"); @@ -139,7 +139,7 @@ function createSourceSidebar() { currentFile, hasFoundFile); }); - container.insertBefore(sidebar, document.querySelector(".sidebar-logo").nextSibling); + container.appendChild(sidebar); // Focus on the current file in the source files sidebar. var selected_elem = sidebar.getElementsByClassName("selected")[0]; if (typeof selected_elem !== "undefined") { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index d8b3ba92dc..ccf3d0a581 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -15,7 +15,7 @@ var settingsDataset = (function () { })(); function getSettingValue(settingName) { - var current = getCurrentValue('rustdoc-' + settingName); + var current = getCurrentValue(settingName); if (current !== null) { return current; } @@ -106,7 +106,7 @@ function hasOwnPropertyRustdoc(obj, property) { function updateLocalStorage(name, value) { try { - window.localStorage.setItem(name, value); + window.localStorage.setItem("rustdoc-" + name, value); } catch(e) { // localStorage is not accessible, do nothing } @@ -114,7 +114,7 @@ function updateLocalStorage(name, value) { function getCurrentValue(name) { try { - return window.localStorage.getItem(name); + return window.localStorage.getItem("rustdoc-" + name); } catch(e) { return null; } @@ -127,7 +127,7 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) { // If this new value comes from a system setting or from the previously // saved theme, no need to save it. if (saveTheme) { - updateLocalStorage("rustdoc-theme", newTheme); + updateLocalStorage("theme", newTheme); } if (styleElem.href === newHref) { @@ -158,7 +158,7 @@ function useSystemTheme(value) { value = true; } - updateLocalStorage("rustdoc-use-system-theme", value); + updateLocalStorage("use-system-theme", value); // update the toggle if we're on the settings page var toggle = document.getElementById("use-system-theme"); @@ -187,22 +187,25 @@ var updateSystemTheme = (function() { var mql = window.matchMedia("(prefers-color-scheme: dark)"); function handlePreferenceChange(mql) { + let use = function(theme) { + switchTheme(window.currentTheme, window.mainTheme, theme, true); + }; // maybe the user has disabled the setting in the meantime! if (getSettingValue("use-system-theme") !== "false") { var lightTheme = getSettingValue("preferred-light-theme") || "light"; var darkTheme = getSettingValue("preferred-dark-theme") || "dark"; if (mql.matches) { - // prefers a dark theme - switchTheme(window.currentTheme, window.mainTheme, darkTheme, true); + use(darkTheme); } else { // prefers a light theme, or has no preference - switchTheme(window.currentTheme, window.mainTheme, lightTheme, true); + use(lightTheme); } - // note: we save the theme so that it doesn't suddenly change when // the user disables "use-system-theme" and reloads the page or // navigates to another page + } else { + use(getSettingValue("theme")); } } @@ -213,22 +216,41 @@ var updateSystemTheme = (function() { }; })(); +function switchToSavedTheme() { + switchTheme( + window.currentTheme, + window.mainTheme, + getSettingValue("theme") || "light", + false + ); +} + if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { // update the preferred dark theme if the user is already using a dark theme // See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732 if (getSettingValue("use-system-theme") === null && getSettingValue("preferred-dark-theme") === null && darkThemes.indexOf(localStoredTheme) >= 0) { - updateLocalStorage("rustdoc-preferred-dark-theme", localStoredTheme); + updateLocalStorage("preferred-dark-theme", localStoredTheme); } // call the function to initialize the theme at least once! updateSystemTheme(); } else { - switchTheme( - window.currentTheme, - window.mainTheme, - getSettingValue("theme") || "light", - false - ); + switchToSavedTheme(); } + +// If we navigate away (for example to a settings page), and then use the back or +// forward button to get back to a page, the theme may have changed in the meantime. +// But scripts may not be re-loaded in such a case due to the bfcache +// (https://web.dev/bfcache/). The "pageshow" event triggers on such navigations. +// Use that opportunity to update the theme. +// We use a setTimeout with a 0 timeout here to put the change on the event queue. +// For some reason, if we try to change the theme while the `pageshow` event is +// running, it sometimes fails to take effect. The problem manifests on Chrome, +// specifically when talking to a remote website with no caching. +window.addEventListener("pageshow", function(ev) { + if (ev.persisted) { + setTimeout(switchToSavedTheme, 0); + } +}); diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 56c5399d07..cd369a93d8 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -67,8 +67,9 @@ crate static LICENSE_APACHE: &[u8] = include_bytes!("static/LICENSE-APACHE.txt") /// The contents of `LICENSE-MIT.txt`, the text of the MIT License. crate static LICENSE_MIT: &[u8] = include_bytes!("static/LICENSE-MIT.txt"); -/// The contents of `rust-logo.png`, the default icon of the documentation. -crate static RUST_LOGO: &[u8] = include_bytes!("static/images/rust-logo.png"); +/// The contents of `rust-logo.svg`, the default icon of the documentation. +crate static RUST_LOGO_SVG: &[u8] = include_bytes!("static/images/rust-logo.svg"); + /// The default documentation favicons (SVG and PNG fallbacks) crate static RUST_FAVICON_SVG: &[u8] = include_bytes!("static/images/favicon.svg"); crate static RUST_FAVICON_PNG_16: &[u8] = include_bytes!("static/images/favicon-16x16.png"); diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 00b46b1ba9..baadd3c27b 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -7,20 +7,20 @@ {#- -#} {#- -#} {{page.title}} {#- -#} - {#- -#} - {#- -#} - {#- -#} - {#- -#} - {#- -#} - {#- -#} + {#- -#} + {#- -#} + {#- -#} + {#- -#} + {#- -#} + {#- -#} {#- -#} + href="{{static_root_path|safe}}normalize{{page.resource_suffix}}.css"> {#- -#} {#- -#} {%- for theme in themes -%} {%- endfor -%} {#- -#} - {#- -#} - {#- -#} - {#- -#} + {#- -#} + {#- -#} + {#- -#} {%- for script in page.static_extra_scripts -%} - {#- -#} + {#- -#} {% endfor %} {%- if layout.scrape_examples_extension -%} - {#- -#} + {#- -#} {%- endif -%} {%- for script in page.extra_scripts -%} - {#- -#} + {#- -#} {% endfor %} {#- -#} - {%- if layout.css_file_extension -%} + {%- if layout.css_file_extension.is_some() -%} {#- -#} + href="{{static_root_path|safe}}theme{{page.resource_suffix}}.css"> {#- -#} {%- endif -%} - {%- if layout.favicon -%} + {%- if !layout.favicon.is_empty() -%} {#- -#} {%- else -%} {#- -#} + href="{{static_root_path|safe}}favicon-16x16{{page.resource_suffix}}.png"> {#- -#} {#- -#} + href="{{static_root_path|safe}}favicon-32x32{{page.resource_suffix}}.png"> {#- -#} {#- -#} + href="{{static_root_path|safe}}favicon{{page.resource_suffix}}.svg"> {#- -#} {%- endif -%} - {{- layout.external_html.in_header | safe -}} + {{- layout.external_html.in_header|safe -}} {#- -#} {#- -#} {#- -#} - {{- layout.external_html.before_content | safe -}} -

for &'a P { + #[inline(always)] + fn into_query_param(self) -> P { + *self + } + } + impl IntoQueryParam for LocalDefId { #[inline(always)] fn into_query_param(self) -> DefId { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 63ed318cad..7c57d42631 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -7,7 +7,7 @@ use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar}; use crate::ty::error::{ExpectedFound, TypeError}; use crate::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; -use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, Term, Ty, TyCtxt, TypeFoldable}; use rustc_hir as ast; use rustc_hir::def_id::DefId; use rustc_span::DUMMY_SP; @@ -89,9 +89,9 @@ pub trait TypeRelation<'tcx>: Sized { fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>; + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>>; fn binders( &mut self, @@ -149,8 +149,8 @@ pub fn relate_substs<'tcx, R: TypeRelation<'tcx>>( Some((ty_def_id, variances)) => { let variance = variances[i]; let variance_info = if variance == ty::Invariant { - let ty = - cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).subst(tcx, a_subst)); + let ty = *cached_ty + .get_or_insert_with(|| tcx.type_of(ty_def_id).subst(tcx, a_subst)); ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } } else { ty::VarianceDiagInfo::default() @@ -291,11 +291,11 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> { b.item_def_id, ))) } else { - let ty = relation.relate_with_variance( + let term = relation.relate_with_variance( ty::Invariant, ty::VarianceDiagInfo::default(), - a.ty, - b.ty, + a.term, + b.term, )?; let substs = relation.relate_with_variance( ty::Invariant, @@ -303,7 +303,7 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> { a.substs, b.substs, )?; - Ok(ty::ExistentialProjection { item_def_id: a.item_def_id, substs, ty }) + Ok(ty::ExistentialProjection { item_def_id: a.item_def_id, substs, term }) } } } @@ -545,16 +545,16 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>( /// it. pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( relation: &mut R, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, -) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, +) -> RelateResult<'tcx, ty::Const<'tcx>> { debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); let tcx = relation.tcx(); // FIXME(oli-obk): once const generics can have generic types, this assertion // will likely get triggered. Move to `normalize_erasing_regions` at that point. - let a_ty = tcx.erase_regions(a.ty); - let b_ty = tcx.erase_regions(b.ty); + let a_ty = tcx.erase_regions(a.ty()); + let b_ty = tcx.erase_regions(b.ty()); if a_ty != b_ty { relation.tcx().sess.delay_span_bug( DUMMY_SP, @@ -562,14 +562,14 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( ); } - let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env()); + let eagerly_eval = |x: ty::Const<'tcx>| x.eval(tcx, relation.param_env()); let a = eagerly_eval(a); let b = eagerly_eval(b); // Currently, the values that can be unified are primitive types, // and those that derive both `PartialEq` and `Eq`, corresponding // to structural-match types. - let is_match = match (a.val, b.val) { + let is_match = match (a.val(), b.val()) { (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { // The caller should handle these cases! bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b) @@ -599,16 +599,16 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( let substs = relation.relate_with_variance( ty::Variance::Invariant, ty::VarianceDiagInfo::default(), - au.substs(tcx), - bu.substs(tcx), + au.substs, + bu.substs, )?; - return Ok(tcx.mk_const(ty::Const { + return Ok(tcx.mk_const(ty::ConstS { val: ty::ConstKind::Unevaluated(ty::Unevaluated { def: au.def, - substs_: Some(substs), + substs, promoted: au.promoted, }), - ty: a.ty, + ty: a.ty(), })); } _ => false, @@ -621,8 +621,8 @@ fn check_const_value_eq<'tcx, R: TypeRelation<'tcx>>( a_val: ConstValue<'tcx>, b_val: ConstValue<'tcx>, // FIXME(oli-obk): these arguments should go away with valtrees - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, // FIXME(oli-obk): this should just be `bool` with valtrees ) -> RelateResult<'tcx, bool> { let tcx = relation.tcx(); @@ -648,9 +648,9 @@ fn check_const_value_eq<'tcx, R: TypeRelation<'tcx>>( } (ConstValue::ByRef { alloc: alloc_a, .. }, ConstValue::ByRef { alloc: alloc_b, .. }) - if a.ty.is_ref() || b.ty.is_ref() => + if a.ty().is_ref() || b.ty().is_ref() => { - if a.ty.is_ref() && b.ty.is_ref() { + if a.ty().is_ref() && b.ty().is_ref() { alloc_a == alloc_b } else { false @@ -663,7 +663,7 @@ fn check_const_value_eq<'tcx, R: TypeRelation<'tcx>>( // Both the variant and each field have to be equal. if a_destructured.variant == b_destructured.variant { for (a_field, b_field) in iter::zip(a_destructured.fields, b_destructured.fields) { - relation.consts(a_field, b_field)?; + relation.consts(*a_field, *b_field)?; } true @@ -756,12 +756,12 @@ impl<'tcx> Relate<'tcx> for ty::Region<'tcx> { } } -impl<'tcx> Relate<'tcx> for &'tcx ty::Const<'tcx> { +impl<'tcx> Relate<'tcx> for ty::Const<'tcx> { fn relate>( relation: &mut R, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { relation.consts(a, b) } } @@ -833,6 +833,20 @@ impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> { } } +impl<'tcx> Relate<'tcx> for ty::Term<'tcx> { + fn relate>( + relation: &mut R, + a: Self, + b: Self, + ) -> RelateResult<'tcx, Self> { + Ok(match (a, b) { + (Term::Ty(a), Term::Ty(b)) => relation.relate(a, b)?.into(), + (Term::Const(a), Term::Const(b)) => relation.relate(a, b)?.into(), + _ => return Err(TypeError::Mismatch), + }) + } +} + impl<'tcx> Relate<'tcx> for ty::ProjectionPredicate<'tcx> { fn relate>( relation: &mut R, @@ -841,7 +855,7 @@ impl<'tcx> Relate<'tcx> for ty::ProjectionPredicate<'tcx> { ) -> RelateResult<'tcx, ty::ProjectionPredicate<'tcx>> { Ok(ty::ProjectionPredicate { projection_ty: relation.relate(a.projection_ty, b.projection_ty)?, - ty: relation.relate(a.ty, b.ty)?, + term: relation.relate(a.term, b.term)?, }) } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 98b1a8b4d7..e4691dee77 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -6,8 +6,9 @@ use crate::mir::interpret; use crate::mir::ProjectionKind; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeVisitor}; use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer}; -use crate::ty::{self, InferConst, Lift, Ty, TyCtxt}; +use crate::ty::{self, InferConst, Lift, Term, Ty, TyCtxt}; use rustc_data_structures::functor::IdFunctor; +use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_hir::def_id::CRATE_DEF_INDEX; use rustc_index::vec::{Idx, IndexVec}; @@ -47,12 +48,6 @@ impl fmt::Debug for ty::UpvarId { } } -impl<'tcx> fmt::Debug for ty::UpvarBorrow<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "UpvarBorrow({:?}, {:?})", self.kind, self.region) - } -} - impl<'tcx> fmt::Debug for ty::ExistentialTraitRef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) @@ -164,7 +159,7 @@ impl<'tcx> fmt::Debug for ty::TraitPredicate<'tcx> { impl<'tcx> fmt::Debug for ty::ProjectionPredicate<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.ty) + write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.term) } } @@ -191,7 +186,7 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> { write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind) } ty::PredicateKind::ConstEvaluatable(uv) => { - write!(f, "ConstEvaluatable({:?}, {:?})", uv.def, uv.substs_) + write!(f, "ConstEvaluatable({:?}, {:?})", uv.def, uv.substs) } ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), ty::PredicateKind::TypeWellFormedFromEnv(ty) => { @@ -225,7 +220,6 @@ TrivialTypeFoldableAndLiftImpls! { ::rustc_hir::def_id::DefId, ::rustc_hir::def_id::LocalDefId, ::rustc_hir::HirId, - ::rustc_hir::LlvmInlineAsmInner, ::rustc_hir::MatchSource, ::rustc_hir::Mutability, ::rustc_hir::Unsafety, @@ -260,6 +254,7 @@ TrivialTypeFoldableAndLiftImpls! { crate::ty::UniverseIndex, crate::ty::Variance, ::rustc_span::Span, + ::rustc_errors::ErrorReported, } /////////////////////////////////////////////////////////////////////////// @@ -363,6 +358,16 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> { } } +impl<'a, 'tcx> Lift<'tcx> for Term<'a> { + type Lifted = ty::Term<'tcx>; + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + Some(match self { + Term::Ty(ty) => Term::Ty(tcx.lift(ty)?), + Term::Const(c) => Term::Const(tcx.lift(c)?), + }) + } +} + impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> { type Lifted = ty::TraitPredicate<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { @@ -410,8 +415,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionTy<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionPredicate<'a> { type Lifted = ty::ProjectionPredicate<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift((self.projection_ty, self.ty)) - .map(|(projection_ty, ty)| ty::ProjectionPredicate { projection_ty, ty }) + tcx.lift((self.projection_ty, self.term)) + .map(|(projection_ty, term)| ty::ProjectionPredicate { projection_ty, term }) } } @@ -420,7 +425,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialProjection<'a> { fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { tcx.lift(self.substs).map(|substs| ty::ExistentialProjection { substs, - ty: tcx.lift(self.ty).expect("type must lift when substs do"), + term: tcx.lift(self.term).expect("type must lift when substs do"), item_def_id: self.item_def_id, }) } @@ -659,14 +664,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { /////////////////////////////////////////////////////////////////////////// // TypeFoldable implementations. -// -// Ideally, each type should invoke `folder.fold_foo(self)` and -// nothing else. In some cases, though, we haven't gotten around to -// adding methods on the `folder` yet, and thus the folding is -// hard-coded here. This is less-flexible, because folders cannot -// override the behavior, but there are a lot of random types and one -// can easily refactor the folding into the TypeFolder trait as -// needed. /// AdtDefs are basically the same as a DefId. impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef { @@ -1074,7 +1071,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { } fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - visitor.visit_ty(self) + visitor.visit_ty(*self) } } @@ -1108,12 +1105,12 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { self, folder: &mut F, ) -> Result { - let new = self.inner.kind.try_fold_with(folder)?; + let new = self.kind().try_fold_with(folder)?; Ok(folder.tcx().reuse_or_mk_predicate(self, new)) } fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.inner.kind.visit_with(visitor) + self.kind().visit_with(visitor) } fn visit_with>(&self, visitor: &mut V) -> ControlFlow { @@ -1121,11 +1118,11 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { } fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.inner.outer_exclusive_binder > binder + self.outer_exclusive_binder() > binder } fn has_type_flags(&self, flags: ty::TypeFlags) -> bool { - self.inner.flags.intersects(flags) + self.flags().intersects(flags) } } @@ -1155,15 +1152,15 @@ impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec } } -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { +impl<'tcx> TypeFoldable<'tcx> for ty::Const<'tcx> { fn try_super_fold_with>( self, folder: &mut F, ) -> Result { - let ty = self.ty.try_fold_with(folder)?; - let val = self.val.try_fold_with(folder)?; - if ty != self.ty || val != self.val { - Ok(folder.tcx().mk_const(ty::Const { ty, val })) + let ty = self.ty().try_fold_with(folder)?; + let val = self.val().try_fold_with(folder)?; + if ty != self.ty() || val != self.val() { + Ok(folder.tcx().mk_const(ty::ConstS { ty, val })) } else { Ok(self) } @@ -1174,12 +1171,12 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { } fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.ty.visit_with(visitor)?; - self.val.visit_with(visitor) + self.ty().visit_with(visitor)?; + self.val().visit_with(visitor) } fn visit_with>(&self, visitor: &mut V) -> ControlFlow { - visitor.visit_const(self) + visitor.visit_const(*self) } } @@ -1232,7 +1229,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx> { ) -> Result { Ok(ty::Unevaluated { def: self.def, - substs_: Some(self.substs(folder.tcx()).try_fold_with(folder)?), + substs: self.substs.try_fold_with(folder)?, promoted: self.promoted, }) } @@ -1242,14 +1239,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx> { } fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow { - if let Some(tcx) = visitor.tcx_for_anon_const_substs() { - self.substs(tcx).visit_with(visitor) - } else if let Some(substs) = self.substs_ { - substs.visit_with(visitor) - } else { - debug!("ignoring default substs of `{:?}`", self.def); - ControlFlow::CONTINUE - } + self.substs.visit_with(visitor) } } @@ -1260,7 +1250,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx, ()> { ) -> Result { Ok(ty::Unevaluated { def: self.def, - substs_: Some(self.substs(folder.tcx()).try_fold_with(folder)?), + substs: self.substs.try_fold_with(folder)?, promoted: self.promoted, }) } @@ -1270,13 +1260,16 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Unevaluated<'tcx, ()> { } fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow { - if let Some(tcx) = visitor.tcx_for_anon_const_substs() { - self.substs(tcx).visit_with(visitor) - } else if let Some(substs) = self.substs_ { - substs.visit_with(visitor) - } else { - debug!("ignoring default substs of `{:?}`", self.def); - ControlFlow::CONTINUE - } + self.substs.visit_with(visitor) + } +} + +impl<'tcx> TypeFoldable<'tcx> for hir::Constness { + fn try_super_fold_with>(self, _: &mut F) -> Result { + Ok(self) + } + + fn super_visit_with>(&self, _: &mut V) -> ControlFlow { + ControlFlow::CONTINUE } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c24a1d8eb5..7c6d6ea1cb 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -8,10 +8,13 @@ use crate::infer::canonical::Canonical; use crate::ty::fold::ValidateBoundVars; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; use crate::ty::InferTy::{self, *}; -use crate::ty::{self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable}; -use crate::ty::{DelaySpanBugEmitted, List, ParamEnv, TyS}; +use crate::ty::{ + self, AdtDef, DefIdTree, Discr, Term, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitor, +}; +use crate::ty::{DelaySpanBugEmitted, List, ParamEnv}; use polonius_engine::Atom; use rustc_data_structures::captures::Captures; +use rustc_data_structures::intern::Interned; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::vec::Idx; @@ -21,8 +24,9 @@ use rustc_target::abi::VariantIdx; use rustc_target::spec::abi; use std::borrow::Cow; use std::cmp::Ordering; +use std::fmt; use std::marker::PhantomData; -use std::ops::Range; +use std::ops::{ControlFlow, Deref, Range}; use ty::util::IntTypeExt; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] @@ -74,10 +78,10 @@ impl BoundRegionKind { } } -/// Defines the kinds of types. +/// Defines the kinds of types used by the type system. /// -/// N.B., if you change this, you'll probably want to change the corresponding -/// AST structure in `rustc_ast/src/ast.rs` as well. +/// Types written by the user start out as [hir::TyKind](rustc_hir::TyKind) and get +/// converted to this representation using `AstConv::ast_ty_to_ty`. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable, Debug)] #[derive(HashStable)] #[rustc_diagnostic_item = "TyKind"] @@ -100,10 +104,11 @@ pub enum TyKind<'tcx> { /// Algebraic data types (ADT). For example: structures, enumerations and unions. /// - /// InternalSubsts here, possibly against intuition, *may* contain `Param`s. - /// That is, even after substitution it is possible that there are type - /// variables. This happens when the `Adt` corresponds to an ADT - /// definition and not a concrete use of it. + /// For example, the type `List` would be represented using the `AdtDef` + /// for `struct List` and the substs `[i32]`. + /// + /// Note that generic parameters in fields only get lazily substituted + /// by using something like `adt_def.all_fields().map(|field| field.ty(tcx, substs))`. Adt(&'tcx AdtDef, SubstsRef<'tcx>), /// An unsized FFI type that is opaque to Rust. Written as `extern type T`. @@ -112,8 +117,8 @@ pub enum TyKind<'tcx> { /// The pointee of a string slice. Written as `str`. Str, - /// An array with the given length. Written as `[T; n]`. - Array(Ty<'tcx>, &'tcx ty::Const<'tcx>), + /// An array with the given length. Written as `[T; N]`. + Array(Ty<'tcx>, ty::Const<'tcx>), /// The pointee of an array slice. Written as `[T]`. Slice(Ty<'tcx>), @@ -126,11 +131,12 @@ pub enum TyKind<'tcx> { Ref(Region<'tcx>, Ty<'tcx>, hir::Mutability), /// The anonymous type of a function declaration/definition. Each - /// function has a unique type, which is output (for a function - /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`. + /// function has a unique type. /// - /// For example the type of `bar` here: + /// For the function `fn foo() -> i32 { 3 }` this type would be + /// shown to the user as `fn() -> i32 {foo}`. /// + /// For example the type of `bar` here: /// ```rust /// fn foo() -> i32 { 1 } /// let bar = foo; // bar: fn() -> i32 {foo} @@ -139,6 +145,9 @@ pub enum TyKind<'tcx> { /// A pointer to a function. Written as `fn() -> i32`. /// + /// Note that both functions and closures start out as either + /// [FnDef] or [Closure] which can be then be coerced to this variant. + /// /// For example the type of `bar` here: /// /// ```rust @@ -150,23 +159,48 @@ pub enum TyKind<'tcx> { /// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`. Dynamic(&'tcx List>>, ty::Region<'tcx>), - /// The anonymous type of a closure. Used to represent the type of - /// `|a| a`. + /// The anonymous type of a closure. Used to represent the type of `|a| a`. + /// + /// Closure substs contain both the - potentially substituted - generic parameters + /// of its parent and some synthetic parameters. See the documentation for + /// [ClosureSubsts] for more details. Closure(DefId, SubstsRef<'tcx>), /// The anonymous type of a generator. Used to represent the type of /// `|a| yield a`. + /// + /// For more info about generator substs, visit the documentation for + /// [GeneratorSubsts]. Generator(DefId, SubstsRef<'tcx>, hir::Movability), /// A type representing the types stored inside a generator. - /// This should only appear in GeneratorInteriors. + /// This should only appear as part of the [GeneratorSubsts]. + /// + /// Note that the captured variables for generators are stored separately + /// using a tuple in the same way as for closures. + /// + /// Unlike upvars, the witness can reference lifetimes from + /// inside of the generator itself. To deal with them in + /// the type of the generator, we convert them to higher ranked + /// lifetimes bound by the witness itself. + /// + /// Looking at the following example, the witness for this generator + /// may end up as something like `for<'a> [Vec, &'a Vec]`: + /// + /// ```rust + /// |a| { + /// let x = &vec![3]; + /// yield a; + /// yield x[0]; + /// } + /// ``` GeneratorWitness(Binder<'tcx, &'tcx List>>), /// The never type `!`. Never, /// A tuple type. For example, `(i32, bool)`. - /// Use `TyS::tuple_fields` to iterate over the field types. + /// Use `Ty::tuple_fields` to iterate over the field types. Tuple(SubstsRef<'tcx>), /// The projection of an associated type. For example, @@ -174,23 +208,44 @@ pub enum TyKind<'tcx> { Projection(ProjectionTy<'tcx>), /// Opaque (`impl Trait`) type found in a return type. + /// /// The `DefId` comes either from /// * the `impl Trait` ast::Ty node, /// * or the `type Foo = impl Trait` declaration - /// The substitutions are for the generics of the function in question. - /// After typeck, the concrete type can be found in the `types` map. + /// + /// For RPIT the substitutions are for the generics of the function, + /// while for TAIT it is used for the generic parameters of the alias. + /// + /// During codegen, `tcx.type_of(def_id)` can be used to get the underlying type. Opaque(DefId, SubstsRef<'tcx>), /// A type parameter; for example, `T` in `fn f(x: T) {}`. Param(ParamTy), - /// Bound type variable, used only when preparing a trait query. + /// Bound type variable, used to represent the `'a` in `for<'a> fn(&'a ())`. + /// + /// For canonical queries, we replace inference variables with bound variables, + /// so e.g. when checking whether `&'_ (): Trait<_>` holds, we canonicalize that to + /// `for<'a, T> &'a (): Trait` and then convert the introduced bound variables + /// back to inference variables in a new inference context when inside of the query. + /// + /// See the `rustc-dev-guide` for more details about + /// [higher-ranked trait bounds][1] and [canonical queries][2]. + /// + /// [1]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + /// [2]: https://rustc-dev-guide.rust-lang.org/traits/canonical-queries.html Bound(ty::DebruijnIndex, BoundTy), - /// A placeholder type - universally quantified higher-ranked type. + /// A placeholder type, used during higher ranked subtyping to instantiate + /// bound variables. Placeholder(ty::PlaceholderType), /// A type variable used during type checking. + /// + /// Similar to placeholders, inference variables also live in a universe to + /// correctly deal with higher ranked types. Though unlike placeholders, + /// that universe is stored in the `InferCtxt` instead of directly + /// inside of the type. Infer(InferTy), /// A placeholder for a type which could not be computed; this is @@ -231,7 +286,7 @@ static_assert_size!(TyKind<'_>, 32); /// in scope on the function that defined the closure, /// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This /// is rather hackily encoded via a scalar type. See -/// `TyS::to_opt_closure_kind` for details. +/// `Ty::to_opt_closure_kind` for details. /// - CS represents the *closure signature*, representing as a `fn()` /// type. For example, `fn(u32, u32) -> u32` would mean that the closure /// implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait @@ -1340,7 +1395,24 @@ impl ParamConst { } } -pub type Region<'tcx> = &'tcx RegionKind; +/// Use this rather than `TyKind`, whenever possible. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] +#[cfg_attr(not(bootstrap), rustc_pass_by_value)] +pub struct Region<'tcx>(pub Interned<'tcx, RegionKind>); + +impl<'tcx> Deref for Region<'tcx> { + type Target = RegionKind; + + fn deref(&self) -> &RegionKind { + &self.0.0 + } +} + +impl<'tcx> fmt::Debug for Region<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.kind()) + } +} /// Representation of regions. Note that the NLL checker uses a distinct /// representation of regions. For this reason, it internally replaces all the @@ -1348,6 +1420,9 @@ pub type Region<'tcx> = &'tcx RegionKind; /// to index into internal NLL data structures. See `rustc_const_eval::borrow_check` /// module for more information. /// +/// Note: operations are on the wrapper `Region` type, which is interned, +/// rather than this type. +/// /// ## The Region lattice within a given function /// /// In general, the region lattice looks like @@ -1464,11 +1539,11 @@ pub enum RegionKind { /// Static data that has an "infinite" lifetime. Top in the region lattice. ReStatic, - /// A region variable. Should not exist after typeck. + /// A region variable. Should not exist outside of type inference. ReVar(RegionVid), /// A placeholder region -- basically, the higher-ranked version of `ReFree`. - /// Should not exist after typeck. + /// Should not exist outside of type inference. RePlaceholder(ty::PlaceholderRegion), /// Empty lifetime is for data that is never accessed. We tag the @@ -1540,7 +1615,7 @@ impl From for BoundTy { pub struct ExistentialProjection<'tcx> { pub item_def_id: DefId, pub substs: SubstsRef<'tcx>, - pub ty: Ty<'tcx>, + pub term: Term<'tcx>, } pub type PolyExistentialProjection<'tcx> = Binder<'tcx, ExistentialProjection<'tcx>>; @@ -1570,7 +1645,7 @@ impl<'tcx> ExistentialProjection<'tcx> { item_def_id: self.item_def_id, substs: tcx.mk_substs_trait(self_ty, self.substs), }, - ty: self.ty, + term: self.term, } } @@ -1584,7 +1659,7 @@ impl<'tcx> ExistentialProjection<'tcx> { Self { item_def_id: projection_predicate.projection_ty.item_def_id, substs: tcx.intern_substs(&projection_predicate.projection_ty.substs[1..]), - ty: projection_predicate.ty, + term: projection_predicate.term, } } } @@ -1604,64 +1679,83 @@ impl<'tcx> PolyExistentialProjection<'tcx> { } /// Region utilities -impl RegionKind { +impl<'tcx> Region<'tcx> { + pub fn kind(self) -> RegionKind { + *self.0.0 + } + /// Is this region named by the user? - pub fn has_name(&self) -> bool { + pub fn has_name(self) -> bool { match *self { - RegionKind::ReEarlyBound(ebr) => ebr.has_name(), - RegionKind::ReLateBound(_, br) => br.kind.is_named(), - RegionKind::ReFree(fr) => fr.bound_region.is_named(), - RegionKind::ReStatic => true, - RegionKind::ReVar(..) => false, - RegionKind::RePlaceholder(placeholder) => placeholder.name.is_named(), - RegionKind::ReEmpty(_) => false, - RegionKind::ReErased => false, + ty::ReEarlyBound(ebr) => ebr.has_name(), + ty::ReLateBound(_, br) => br.kind.is_named(), + ty::ReFree(fr) => fr.bound_region.is_named(), + ty::ReStatic => true, + ty::ReVar(..) => false, + ty::RePlaceholder(placeholder) => placeholder.name.is_named(), + ty::ReEmpty(_) => false, + ty::ReErased => false, } } #[inline] - pub fn is_late_bound(&self) -> bool { + pub fn is_static(self) -> bool { + matches!(*self, ty::ReStatic) + } + + #[inline] + pub fn is_erased(self) -> bool { + matches!(*self, ty::ReErased) + } + + #[inline] + pub fn is_late_bound(self) -> bool { matches!(*self, ty::ReLateBound(..)) } #[inline] - pub fn is_placeholder(&self) -> bool { + pub fn is_placeholder(self) -> bool { matches!(*self, ty::RePlaceholder(..)) } #[inline] - pub fn bound_at_or_above_binder(&self, index: ty::DebruijnIndex) -> bool { + pub fn is_empty(self) -> bool { + matches!(*self, ty::ReEmpty(..)) + } + + #[inline] + pub fn bound_at_or_above_binder(self, index: ty::DebruijnIndex) -> bool { match *self { ty::ReLateBound(debruijn, _) => debruijn >= index, _ => false, } } - pub fn type_flags(&self) -> TypeFlags { + pub fn type_flags(self) -> TypeFlags { let mut flags = TypeFlags::empty(); match *self { ty::ReVar(..) => { - flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS; - flags = flags | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS; + flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; } ty::RePlaceholder(..) => { - flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS; - flags = flags | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS; + flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PLACEHOLDER; } ty::ReEarlyBound(..) => { - flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS; - flags = flags | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS; - flags = flags | TypeFlags::HAS_KNOWN_RE_PARAM; + flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags = flags | TypeFlags::HAS_RE_PARAM; } ty::ReFree { .. } => { - flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS; - flags = flags | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS; + flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; } ty::ReEmpty(_) | ty::ReStatic => { - flags = flags | TypeFlags::HAS_KNOWN_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_REGIONS; } ty::ReLateBound(..) => { flags = flags | TypeFlags::HAS_RE_LATE_BOUND; @@ -1695,8 +1789,8 @@ impl RegionKind { /// of the impl, and for all the other highlighted regions, it /// would return the `DefId` of the function. In other cases (not shown), this /// function might return the `DefId` of a closure. - pub fn free_region_binding_scope(&self, tcx: TyCtxt<'_>) -> DefId { - match self { + pub fn free_region_binding_scope(self, tcx: TyCtxt<'_>) -> DefId { + match *self { ty::ReEarlyBound(br) => tcx.parent(br.def_id).unwrap(), ty::ReFree(fr) => fr.scope, _ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self), @@ -1705,19 +1799,19 @@ impl RegionKind { } /// Type utilities -impl<'tcx> TyS<'tcx> { +impl<'tcx> Ty<'tcx> { #[inline(always)] - pub fn kind(&self) -> &TyKind<'tcx> { - &self.kind + pub fn kind(self) -> &'tcx TyKind<'tcx> { + &self.0.0.kind } #[inline(always)] - pub fn flags(&self) -> TypeFlags { - self.flags + pub fn flags(self) -> TypeFlags { + self.0.0.flags } #[inline] - pub fn is_unit(&self) -> bool { + pub fn is_unit(self) -> bool { match self.kind() { Tuple(ref tys) => tys.is_empty(), _ => false, @@ -1725,32 +1819,32 @@ impl<'tcx> TyS<'tcx> { } #[inline] - pub fn is_never(&self) -> bool { + pub fn is_never(self) -> bool { matches!(self.kind(), Never) } #[inline] - pub fn is_primitive(&self) -> bool { + pub fn is_primitive(self) -> bool { self.kind().is_primitive() } #[inline] - pub fn is_adt(&self) -> bool { + pub fn is_adt(self) -> bool { matches!(self.kind(), Adt(..)) } #[inline] - pub fn is_ref(&self) -> bool { + pub fn is_ref(self) -> bool { matches!(self.kind(), Ref(..)) } #[inline] - pub fn is_ty_var(&self) -> bool { + pub fn is_ty_var(self) -> bool { matches!(self.kind(), Infer(TyVar(_))) } #[inline] - pub fn ty_vid(&self) -> Option { + pub fn ty_vid(self) -> Option { match self.kind() { &Infer(TyVar(vid)) => Some(vid), _ => None, @@ -1758,28 +1852,28 @@ impl<'tcx> TyS<'tcx> { } #[inline] - pub fn is_ty_infer(&self) -> bool { + pub fn is_ty_infer(self) -> bool { matches!(self.kind(), Infer(_)) } #[inline] - pub fn is_phantom_data(&self) -> bool { + pub fn is_phantom_data(self) -> bool { if let Adt(def, _) = self.kind() { def.is_phantom_data() } else { false } } #[inline] - pub fn is_bool(&self) -> bool { + pub fn is_bool(self) -> bool { *self.kind() == Bool } /// Returns `true` if this type is a `str`. #[inline] - pub fn is_str(&self) -> bool { + pub fn is_str(self) -> bool { *self.kind() == Str } #[inline] - pub fn is_param(&self, index: u32) -> bool { + pub fn is_param(self, index: u32) -> bool { match self.kind() { ty::Param(ref data) => data.index == index, _ => false, @@ -1787,7 +1881,7 @@ impl<'tcx> TyS<'tcx> { } #[inline] - pub fn is_slice(&self) -> bool { + pub fn is_slice(self) -> bool { match self.kind() { RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_) | Str), _ => false, @@ -1795,27 +1889,27 @@ impl<'tcx> TyS<'tcx> { } #[inline] - pub fn is_array(&self) -> bool { + pub fn is_array(self) -> bool { matches!(self.kind(), Array(..)) } #[inline] - pub fn is_simd(&self) -> bool { + pub fn is_simd(self) -> bool { match self.kind() { Adt(def, _) => def.repr.simd(), _ => false, } } - pub fn sequence_element_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'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(ty::UintTy::U8), + Array(ty, _) | Slice(ty) => *ty, + Str => tcx.types.u8, _ => bug!("`sequence_element_type` called on non-sequence value: {}", self), } } - pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { + pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { match self.kind() { Adt(def, substs) => { assert!(def.repr.simd(), "`simd_size_and_type` called on non-SIMD type"); @@ -1830,7 +1924,7 @@ impl<'tcx> TyS<'tcx> { // The way we evaluate the `N` in `[T; N]` here only works since we use // `simd_size_and_type` post-monomorphization. It will probably start to ICE // if we use it in generic code. See the `simd-array-trait` ui test. - (f0_len.eval_usize(tcx, ParamEnv::empty()) as u64, f0_elem_ty) + (f0_len.eval_usize(tcx, ParamEnv::empty()) as u64, *f0_elem_ty) } // Otherwise, the fields of this Adt are the SIMD components (and we assume they // all have the same type). @@ -1842,12 +1936,12 @@ impl<'tcx> TyS<'tcx> { } #[inline] - pub fn is_region_ptr(&self) -> bool { + pub fn is_region_ptr(self) -> bool { matches!(self.kind(), Ref(..)) } #[inline] - pub fn is_mutable_ptr(&self) -> bool { + pub fn is_mutable_ptr(self) -> bool { matches!( self.kind(), RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. }) @@ -1857,7 +1951,7 @@ impl<'tcx> TyS<'tcx> { /// Get the mutability of the reference or `None` when not a reference #[inline] - pub fn ref_mutability(&self) -> Option { + pub fn ref_mutability(self) -> Option { match self.kind() { Ref(_, _, mutability) => Some(*mutability), _ => None, @@ -1865,18 +1959,18 @@ impl<'tcx> TyS<'tcx> { } #[inline] - pub fn is_unsafe_ptr(&self) -> bool { + pub fn is_unsafe_ptr(self) -> bool { matches!(self.kind(), RawPtr(_)) } /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). #[inline] - pub fn is_any_ptr(&self) -> bool { + pub fn is_any_ptr(self) -> bool { self.is_region_ptr() || self.is_unsafe_ptr() || self.is_fn_ptr() } #[inline] - pub fn is_box(&self) -> bool { + pub fn is_box(self) -> bool { match self.kind() { Adt(def, _) => def.is_box(), _ => false, @@ -1884,7 +1978,7 @@ impl<'tcx> TyS<'tcx> { } /// Panics if called on any type other than `Box`. - pub fn boxed_ty(&self) -> Ty<'tcx> { + pub fn boxed_ty(self) -> Ty<'tcx> { match self.kind() { Adt(def, substs) if def.is_box() => substs.type_at(0), _ => bug!("`boxed_ty` is called on non-box type {:?}", self), @@ -1895,7 +1989,7 @@ impl<'tcx> TyS<'tcx> { /// (A RawPtr is scalar because it represents a non-managed pointer, so its /// contents are abstract to rustc.) #[inline] - pub fn is_scalar(&self) -> bool { + pub fn is_scalar(self) -> bool { matches!( self.kind(), Bool | Char @@ -1911,99 +2005,117 @@ impl<'tcx> TyS<'tcx> { /// Returns `true` if this type is a floating point type. #[inline] - pub fn is_floating_point(&self) -> bool { + pub fn is_floating_point(self) -> bool { matches!(self.kind(), Float(_) | Infer(FloatVar(_))) } #[inline] - pub fn is_trait(&self) -> bool { + pub fn is_trait(self) -> bool { matches!(self.kind(), Dynamic(..)) } #[inline] - pub fn is_enum(&self) -> bool { + pub fn is_enum(self) -> bool { matches!(self.kind(), Adt(adt_def, _) if adt_def.is_enum()) } #[inline] - pub fn is_union(&self) -> bool { + pub fn is_union(self) -> bool { matches!(self.kind(), Adt(adt_def, _) if adt_def.is_union()) } #[inline] - pub fn is_closure(&self) -> bool { + pub fn is_closure(self) -> bool { matches!(self.kind(), Closure(..)) } #[inline] - pub fn is_generator(&self) -> bool { + pub fn is_generator(self) -> bool { matches!(self.kind(), Generator(..)) } #[inline] - pub fn is_integral(&self) -> bool { + pub fn is_integral(self) -> bool { matches!(self.kind(), Infer(IntVar(_)) | Int(_) | Uint(_)) } #[inline] - pub fn is_fresh_ty(&self) -> bool { + pub fn is_fresh_ty(self) -> bool { matches!(self.kind(), Infer(FreshTy(_))) } #[inline] - pub fn is_fresh(&self) -> bool { + pub fn is_fresh(self) -> bool { matches!(self.kind(), Infer(FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_))) } #[inline] - pub fn is_char(&self) -> bool { + pub fn is_char(self) -> bool { matches!(self.kind(), Char) } #[inline] - pub fn is_numeric(&self) -> bool { + pub fn is_numeric(self) -> bool { self.is_integral() || self.is_floating_point() } #[inline] - pub fn is_signed(&self) -> bool { + pub fn is_signed(self) -> bool { matches!(self.kind(), Int(_)) } #[inline] - pub fn is_ptr_sized_integral(&self) -> bool { + pub fn is_ptr_sized_integral(self) -> bool { matches!(self.kind(), Int(ty::IntTy::Isize) | Uint(ty::UintTy::Usize)) } #[inline] - pub fn has_concrete_skeleton(&self) -> bool { + pub fn has_concrete_skeleton(self) -> bool { !matches!(self.kind(), Param(_) | Infer(_) | Error(_)) } + /// Checks whether a type recursively contains another type + /// + /// Example: `Option<()>` contains `()` + pub fn contains(self, other: Ty<'tcx>) -> bool { + struct ContainsTyVisitor<'tcx>(Ty<'tcx>); + + impl<'tcx> TypeVisitor<'tcx> for ContainsTyVisitor<'tcx> { + type BreakTy = (); + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + if self.0 == t { ControlFlow::BREAK } else { t.super_visit_with(self) } + } + } + + let cf = self.visit_with(&mut ContainsTyVisitor(other)); + cf.is_break() + } + /// Returns the type and mutability of `*ty`. /// /// The parameter `explicit` indicates if this is an *explicit* dereference. /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly. - pub fn builtin_deref(&self, explicit: bool) -> Option> { + pub fn builtin_deref(self, explicit: bool) -> Option> { match self.kind() { Adt(def, _) if def.is_box() => { Some(TypeAndMut { ty: self.boxed_ty(), mutbl: hir::Mutability::Not }) } - Ref(_, ty, mutbl) => Some(TypeAndMut { ty, mutbl: *mutbl }), + Ref(_, ty, mutbl) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }), RawPtr(mt) if explicit => Some(*mt), _ => None, } } /// Returns the type of `ty[i]`. - pub fn builtin_index(&self) -> Option> { + pub fn builtin_index(self) -> Option> { match self.kind() { - Array(ty, _) | Slice(ty) => Some(ty), + Array(ty, _) | Slice(ty) => Some(*ty), _ => None, } } - pub fn fn_sig(&self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { + pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> { match self.kind() { FnDef(def_id, substs) => tcx.fn_sig(*def_id).subst(tcx, substs), FnPtr(f) => *f, @@ -2019,22 +2131,22 @@ impl<'tcx> TyS<'tcx> { } #[inline] - pub fn is_fn(&self) -> bool { + pub fn is_fn(self) -> bool { matches!(self.kind(), FnDef(..) | FnPtr(_)) } #[inline] - pub fn is_fn_ptr(&self) -> bool { + pub fn is_fn_ptr(self) -> bool { matches!(self.kind(), FnPtr(_)) } #[inline] - pub fn is_impl_trait(&self) -> bool { + pub fn is_impl_trait(self) -> bool { matches!(self.kind(), Opaque(..)) } #[inline] - pub fn ty_adt_def(&self) -> Option<&'tcx AdtDef> { + pub fn ty_adt_def(self) -> Option<&'tcx AdtDef> { match self.kind() { Adt(adt, _) => Some(adt), _ => None, @@ -2043,7 +2155,7 @@ impl<'tcx> TyS<'tcx> { /// Iterates over tuple fields. /// Panics when called on anything but a tuple. - pub fn tuple_fields(&self) -> impl DoubleEndedIterator> { + pub fn tuple_fields(self) -> impl DoubleEndedIterator> { match self.kind() { Tuple(substs) => substs.iter().map(|field| field.expect_ty()), _ => bug!("tuple_fields called on non-tuple"), @@ -2052,7 +2164,7 @@ impl<'tcx> TyS<'tcx> { /// Get the `i`-th element of a tuple. /// Panics when called on anything but a tuple. - pub fn tuple_element_ty(&self, i: usize) -> Option> { + pub fn tuple_element_ty(self, i: usize) -> Option> { match self.kind() { Tuple(substs) => substs.iter().nth(i).map(|field| field.expect_ty()), _ => bug!("tuple_fields called on non-tuple"), @@ -2063,7 +2175,7 @@ impl<'tcx> TyS<'tcx> { // // FIXME: This requires the optimized MIR in the case of generators. #[inline] - pub fn variant_range(&self, tcx: TyCtxt<'tcx>) -> Option> { + pub fn variant_range(self, tcx: TyCtxt<'tcx>) -> Option> { match self.kind() { TyKind::Adt(adt, _) => Some(adt.variant_range()), TyKind::Generator(def_id, substs, _) => { @@ -2079,7 +2191,7 @@ impl<'tcx> TyS<'tcx> { // FIXME: This requires the optimized MIR in the case of generators. #[inline] pub fn discriminant_for_variant( - &self, + self, tcx: TyCtxt<'tcx>, variant_index: VariantIdx, ) -> Option> { @@ -2100,7 +2212,7 @@ impl<'tcx> TyS<'tcx> { } /// Returns the type of the discriminant of this type. - pub fn discriminant_ty(&'tcx self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + pub fn discriminant_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match self.kind() { ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), @@ -2143,9 +2255,12 @@ impl<'tcx> TyS<'tcx> { } /// Returns the type of metadata for (potentially fat) pointers to this type. - pub fn ptr_metadata_ty(&'tcx self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - // FIXME: should this normalize? - let tail = tcx.struct_tail_without_normalization(self); + pub fn ptr_metadata_ty( + self, + tcx: TyCtxt<'tcx>, + normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, + ) -> Ty<'tcx> { + let tail = tcx.struct_tail_with_normalize(self, normalize); match tail.kind() { // Sized types ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) @@ -2202,7 +2317,7 @@ impl<'tcx> TyS<'tcx> { /// to represent the closure kind, because it has not yet been /// inferred. Once upvar inference (in `rustc_typeck/src/check/upvar.rs`) /// is complete, that type variable will be unified. - pub fn to_opt_closure_kind(&self) -> Option { + pub fn to_opt_closure_kind(self) -> Option { match self.kind() { Int(int_ty) => match int_ty { ty::IntTy::I8 => Some(ty::ClosureKind::Fn), @@ -2231,7 +2346,7 @@ impl<'tcx> TyS<'tcx> { /// bound such as `[_]: Copy`. A function with such a bound obviously never /// can be called, but that doesn't mean it shouldn't typecheck. This is why /// this method doesn't return `Option`. - pub fn is_trivially_sized(&self, tcx: TyCtxt<'tcx>) -> bool { + pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool { match self.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index a711811491..7dccef5e3e 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -6,6 +6,7 @@ use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitor} use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts}; use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt}; +use rustc_data_structures::intern::Interned; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; use rustc_serialize::{self, Decodable, Encodable}; @@ -25,10 +26,13 @@ use std::ops::ControlFlow; /// To reduce memory usage, a `GenericArg` is an interned pointer, /// with the lowest 2 bits being reserved for a tag to /// indicate the type (`Ty`, `Region`, or `Const`) it points to. +/// +/// Note: the `PartialEq`, `Eq` and `Hash` derives are only valid because `Ty`, +/// `Region` and `Const` are all interned. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct GenericArg<'tcx> { ptr: NonZeroUsize, - marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, &'tcx ty::Const<'tcx>)>, + marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>)>, } const TAG_MASK: usize = 0b11; @@ -40,26 +44,27 @@ const CONST_TAG: usize = 0b10; pub enum GenericArgKind<'tcx> { Lifetime(ty::Region<'tcx>), Type(Ty<'tcx>), - Const(&'tcx ty::Const<'tcx>), + Const(ty::Const<'tcx>), } impl<'tcx> GenericArgKind<'tcx> { + #[inline] fn pack(self) -> GenericArg<'tcx> { let (tag, ptr) = match self { GenericArgKind::Lifetime(lt) => { // Ensure we can use the tag bits. - assert_eq!(mem::align_of_val(lt) & TAG_MASK, 0); - (REGION_TAG, lt as *const _ as usize) + assert_eq!(mem::align_of_val(lt.0.0) & TAG_MASK, 0); + (REGION_TAG, lt.0.0 as *const ty::RegionKind as usize) } GenericArgKind::Type(ty) => { // Ensure we can use the tag bits. - assert_eq!(mem::align_of_val(ty) & TAG_MASK, 0); - (TYPE_TAG, ty as *const _ as usize) + assert_eq!(mem::align_of_val(ty.0.0) & TAG_MASK, 0); + (TYPE_TAG, ty.0.0 as *const ty::TyS<'tcx> as usize) } GenericArgKind::Const(ct) => { // Ensure we can use the tag bits. - assert_eq!(mem::align_of_val(ct) & TAG_MASK, 0); - (CONST_TAG, ct as *const _ as usize) + assert_eq!(mem::align_of_val(ct.0.0) & TAG_MASK, 0); + (CONST_TAG, ct.0.0 as *const ty::ConstS<'tcx> as usize) } }; @@ -90,19 +95,22 @@ impl<'tcx> PartialOrd for GenericArg<'tcx> { } impl<'tcx> From> for GenericArg<'tcx> { + #[inline] fn from(r: ty::Region<'tcx>) -> GenericArg<'tcx> { GenericArgKind::Lifetime(r).pack() } } impl<'tcx> From> for GenericArg<'tcx> { + #[inline] fn from(ty: Ty<'tcx>) -> GenericArg<'tcx> { GenericArgKind::Type(ty).pack() } } -impl<'tcx> From<&'tcx ty::Const<'tcx>> for GenericArg<'tcx> { - fn from(c: &'tcx ty::Const<'tcx>) -> GenericArg<'tcx> { +impl<'tcx> From> for GenericArg<'tcx> { + #[inline] + fn from(c: ty::Const<'tcx>) -> GenericArg<'tcx> { GenericArgKind::Const(c).pack() } } @@ -111,11 +119,20 @@ impl<'tcx> GenericArg<'tcx> { #[inline] pub fn unpack(self) -> GenericArgKind<'tcx> { let ptr = self.ptr.get(); + // SAFETY: use of `Interned::new_unchecked` here is ok because these + // pointers were originally created from `Interned` types in `pack()`, + // and this is just going in the other direction. unsafe { match ptr & TAG_MASK { - REGION_TAG => GenericArgKind::Lifetime(&*((ptr & !TAG_MASK) as *const _)), - TYPE_TAG => GenericArgKind::Type(&*((ptr & !TAG_MASK) as *const _)), - CONST_TAG => GenericArgKind::Const(&*((ptr & !TAG_MASK) as *const _)), + REGION_TAG => GenericArgKind::Lifetime(ty::Region(Interned::new_unchecked( + &*((ptr & !TAG_MASK) as *const ty::RegionKind), + ))), + TYPE_TAG => GenericArgKind::Type(Ty(Interned::new_unchecked( + &*((ptr & !TAG_MASK) as *const ty::TyS<'tcx>), + ))), + CONST_TAG => GenericArgKind::Const(ty::Const(Interned::new_unchecked( + &*((ptr & !TAG_MASK) as *const ty::ConstS<'tcx>), + ))), _ => intrinsics::unreachable(), } } @@ -132,7 +149,7 @@ impl<'tcx> GenericArg<'tcx> { } /// Unpack the `GenericArg` as a const when it is known certainly to be a const. - pub fn expect_const(self) -> &'tcx ty::Const<'tcx> { + pub fn expect_const(self) -> ty::Const<'tcx> { match self.unpack() { GenericArgKind::Const(c) => c, _ => bug!("expected a const, but found another kind"), @@ -180,8 +197,8 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for GenericArg<'tcx> { } impl<'tcx, D: TyDecoder<'tcx>> Decodable for GenericArg<'tcx> { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(GenericArgKind::decode(d)?.pack()) + fn decode(d: &mut D) -> GenericArg<'tcx> { + GenericArgKind::decode(d).pack() } } @@ -275,10 +292,6 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { } } - pub fn is_noop(&self) -> bool { - self.is_empty() - } - #[inline] pub fn types(&'a self) -> impl DoubleEndedIterator> + 'a { self.iter() @@ -293,7 +306,7 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { } #[inline] - pub fn consts(&'a self) -> impl DoubleEndedIterator> + 'a { + pub fn consts(&'a self) -> impl DoubleEndedIterator> + 'a { self.iter().filter_map(|k| { if let GenericArgKind::Const(ct) = k.unpack() { Some(ct) } else { None } }) @@ -328,7 +341,7 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { } #[inline] - pub fn const_at(&self, i: usize) -> &'tcx ty::Const<'tcx> { + pub fn const_at(&self, i: usize) -> ty::Const<'tcx> { if let GenericArgKind::Const(ct) = self[i].unpack() { ct } else { @@ -400,15 +413,7 @@ impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> { } } 0 => Ok(self), - _ => { - let params: SmallVec<[_; 8]> = - self.iter().map(|k| k.try_fold_with(folder)).collect::>()?; - if params[..] == self[..] { - Ok(self) - } else { - Ok(folder.tcx().intern_substs(¶ms)) - } - } + _ => ty::util::fold_list(self, folder, |tcx, v| tcx.intern_substs(v)), } } @@ -505,7 +510,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.potentially_needs_subst() { + if !t.needs_subst() { return t; } @@ -515,8 +520,8 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { } } - fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::ConstKind::Param(p) = c.val { + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Param(p) = c.val() { self.const_for_param(p, c) } else { c.super_fold_with(self) @@ -565,11 +570,7 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { self.shift_vars_through_binders(ty) } - fn const_for_param( - &self, - p: ParamConst, - source_ct: &'tcx ty::Const<'tcx>, - ) -> &'tcx ty::Const<'tcx> { + fn const_for_param(&self, p: ParamConst, source_ct: ty::Const<'tcx>) -> ty::Const<'tcx> { // Look up the const in the substitutions. It really should be in there. let opt_ct = self.substs.get(p.index as usize).map(|k| k.unpack()); let ct = match opt_ct { diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 34d059f4ec..8f4cc182e7 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,7 +1,7 @@ use crate::traits::specialization_graph; -use crate::ty::fast_reject::{self, SimplifiedType, SimplifyParams, StripReferences}; +use crate::ty::fast_reject::{self, SimplifiedType, SimplifyParams}; use crate::ty::fold::TypeFoldable; -use crate::ty::{Ty, TyCtxt}; +use crate::ty::{Ident, Ty, TyCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::definitions::DefPathHash; @@ -44,6 +44,10 @@ pub struct TraitDef { /// The ICH of this trait's DefPath, cached here so it doesn't have to be /// recomputed all the time. pub def_path_hash: DefPathHash, + + /// List of functions from `#[rustc_must_implement_one_of]` attribute one of which + /// must be implemented. + pub must_implement_one_of: Option>, } /// Whether this trait is treated specially by the standard library @@ -87,6 +91,7 @@ impl<'tcx> TraitDef { skip_array_during_method_dispatch: bool, specialization_kind: TraitSpecializationKind, def_path_hash: DefPathHash, + must_implement_one_of: Option>, ) -> TraitDef { TraitDef { def_id, @@ -97,6 +102,7 @@ impl<'tcx> TraitDef { skip_array_during_method_dispatch, specialization_kind, def_path_hash, + must_implement_one_of, } } @@ -138,6 +144,21 @@ impl<'tcx> TyCtxt<'tcx> { }); } + pub fn non_blanket_impls_for_ty( + self, + def_id: DefId, + self_ty: Ty<'tcx>, + ) -> impl Iterator + 'tcx { + let impls = self.trait_impls_of(def_id); + if let Some(simp) = fast_reject::simplify_type(self, self_ty, SimplifyParams::No) { + if let Some(impls) = impls.non_blanket_impls.get(&simp) { + return impls.iter().copied(); + } + } + + [].iter().copied() + } + /// Applies function to every impl that could possibly match the self type `self_ty` and returns /// the first non-none value. pub fn find_map_relevant_impl Option>( @@ -166,9 +187,7 @@ impl<'tcx> TyCtxt<'tcx> { // whose outer level is not a parameter or projection. Especially for things like // `T: Clone` this is incredibly useful as we would otherwise look at all the impls // of `Clone` for `Option`, `Vec`, `ConcreteType` and so on. - if let Some(simp) = - fast_reject::simplify_type(self, self_ty, SimplifyParams::Yes, StripReferences::No) - { + if let Some(simp) = fast_reject::simplify_type(self, self_ty, SimplifyParams::Yes) { if let Some(impls) = impls.non_blanket_impls.get(&simp) { for &impl_def_id in impls { if let result @ Some(_) = f(impl_def_id) { @@ -191,7 +210,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn all_impls(self, def_id: DefId) -> impl Iterator + 'tcx { let TraitImpls { blanket_impls, non_blanket_impls } = self.trait_impls_of(def_id); - blanket_impls.iter().chain(non_blanket_impls.iter().map(|(_, v)| v).flatten()).cloned() + blanket_impls.iter().chain(non_blanket_impls.iter().flat_map(|(_, v)| v)).cloned() } } @@ -228,7 +247,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait } if let Some(simplified_self_ty) = - fast_reject::simplify_type(tcx, impl_self_ty, SimplifyParams::No, StripReferences::No) + fast_reject::simplify_type(tcx, impl_self_ty, SimplifyParams::No) { impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id); } else { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 669065598f..92d9cb2fc1 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -5,16 +5,19 @@ use crate::ty::fold::{FallibleTypeFolder, TypeFolder}; use crate::ty::layout::IntegerExt; use crate::ty::query::TyCtxtAt; use crate::ty::subst::{GenericArgKind, Subst, SubstsRef}; -use crate::ty::TyKind::*; -use crate::ty::{self, DebruijnIndex, DefIdTree, List, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{ + self, Const, DebruijnIndex, DefIdTree, List, ReEarlyBound, Region, Ty, TyCtxt, TyKind::*, + TypeFoldable, +}; use rustc_apfloat::Float as _; use rustc_ast as ast; use rustc_attr::{self as attr, SignedInt, UnsignedInt}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::ErrorReported; use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_macros::HashStable; use rustc_query_system::ich::NodeIdHashingMode; @@ -143,6 +146,37 @@ impl<'tcx> TyCtxt<'tcx> { hasher.finish() } + pub fn res_generics_def_id(self, res: Res) -> Option { + match res { + Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => { + Some(self.parent(def_id).and_then(|def_id| self.parent(def_id)).unwrap()) + } + Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Struct, _), def_id) => { + Some(self.parent(def_id).unwrap()) + } + // Other `DefKind`s don't have generics and would ICE when calling + // `generics_of`. + Res::Def( + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::OpaqueTy + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::Fn + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Impl, + def_id, + ) => Some(def_id), + Res::Err => None, + _ => None, + } + } + pub fn has_error_field(self, ty: Ty<'tcx>) -> bool { if let ty::Adt(def, substs) = *ty.kind() { for field in def.all_fields() { @@ -192,7 +226,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn struct_tail_with_normalize( self, mut ty: Ty<'tcx>, - normalize: impl Fn(Ty<'tcx>) -> Ty<'tcx>, + mut normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, ) -> Ty<'tcx> { let recursion_limit = self.recursion_limit(); for iteration in 0.. { @@ -389,15 +423,17 @@ impl<'tcx> TyCtxt<'tcx> { let result = iter::zip(item_substs, impl_substs) .filter(|&(_, k)| { match k.unpack() { - GenericArgKind::Lifetime(&ty::RegionKind::ReEarlyBound(ref ebr)) => { + GenericArgKind::Lifetime(Region(Interned(ReEarlyBound(ref ebr), _))) => { !impl_generics.region_param(ebr, self).pure_wrt_drop } - GenericArgKind::Type(&ty::TyS { kind: ty::Param(ref pt), .. }) => { - !impl_generics.type_param(pt, self).pure_wrt_drop - } - GenericArgKind::Const(&ty::Const { - val: ty::ConstKind::Param(ref pc), .. - }) => !impl_generics.const_param(pc, self).pure_wrt_drop, + GenericArgKind::Type(Ty(Interned( + ty::TyS { kind: ty::Param(ref pt), .. }, + _, + ))) => !impl_generics.type_param(pt, self).pure_wrt_drop, + GenericArgKind::Const(Const(Interned( + ty::ConstS { val: ty::ConstKind::Param(ref pc), .. }, + _, + ))) => !impl_generics.const_param(pc, self).pure_wrt_drop, GenericArgKind::Lifetime(_) | GenericArgKind::Type(_) | GenericArgKind::Const(_) => { @@ -577,7 +613,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { let substs = substs.fold_with(self); if !self.check_recursion || self.seen_opaque_tys.insert(def_id) { let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) { - Some(expanded_ty) => expanded_ty, + Some(expanded_ty) => *expanded_ty, None => { let generic_ty = self.tcx.type_of(def_id); let concrete_ty = generic_ty.subst(self.tcx, substs); @@ -606,7 +642,7 @@ impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Opaque(def_id, substs) = t.kind { + if let ty::Opaque(def_id, substs) = *t.kind() { self.expand_opaque_ty(def_id, substs).unwrap_or(t) } else if t.has_opaque_types() { t.super_fold_with(self) @@ -616,10 +652,10 @@ impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> { } } -impl<'tcx> ty::TyS<'tcx> { +impl<'tcx> Ty<'tcx> { /// Returns the maximum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_max_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> { + pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option> { let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); @@ -634,12 +670,12 @@ impl<'tcx> ty::TyS<'tcx> { }), _ => None, }; - val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) + val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) } /// Returns the minimum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_min_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> { + pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option> { let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); @@ -653,7 +689,7 @@ impl<'tcx> ty::TyS<'tcx> { }), _ => None, }; - val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) + val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self))) } /// Checks whether values of this type `T` are *moved* or *copied* @@ -664,7 +700,7 @@ impl<'tcx> ty::TyS<'tcx> { /// full requirements for the `Copy` trait (cc #29149) -- this /// winds up being reported as an error during NLL borrow check. pub fn is_copy_modulo_regions( - &'tcx self, + self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> bool { @@ -677,7 +713,7 @@ impl<'tcx> ty::TyS<'tcx> { /// over-approximation in generic contexts, where one can have /// strange rules like `>::Bar: Sized` that /// actually carry lifetime requirements. - pub fn is_sized(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + pub fn is_sized(self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { self.is_trivially_sized(tcx_at.tcx) || tcx_at.is_sized_raw(param_env.and(self)) } @@ -688,7 +724,7 @@ impl<'tcx> ty::TyS<'tcx> { /// optimization as well as the rules around static values. Note /// that the `Freeze` trait is not exposed to end users and is /// effectively an implementation detail. - pub fn is_freeze(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + pub fn is_freeze(self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { self.is_trivially_freeze() || tcx_at.is_freeze_raw(param_env.and(self)) } @@ -696,7 +732,7 @@ impl<'tcx> ty::TyS<'tcx> { /// /// Returning true means the type is known to be `Freeze`. Returning /// `false` means nothing -- could be `Freeze`, might not be. - fn is_trivially_freeze(&self) -> bool { + fn is_trivially_freeze(self) -> bool { match self.kind() { ty::Int(_) | ty::Uint(_) @@ -710,7 +746,7 @@ impl<'tcx> ty::TyS<'tcx> { | ty::FnDef(..) | ty::Error(_) | ty::FnPtr(_) => true, - ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_freeze), + ty::Tuple(_) => self.tuple_fields().all(|f| Self::is_trivially_freeze(f)), ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_freeze(), ty::Adt(..) | ty::Bound(..) @@ -728,7 +764,7 @@ impl<'tcx> ty::TyS<'tcx> { } /// Checks whether values of this type `T` implement the `Unpin` trait. - pub fn is_unpin(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + pub fn is_unpin(self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { self.is_trivially_unpin() || tcx_at.is_unpin_raw(param_env.and(self)) } @@ -736,7 +772,7 @@ impl<'tcx> ty::TyS<'tcx> { /// /// Returning true means the type is known to be `Unpin`. Returning /// `false` means nothing -- could be `Unpin`, might not be. - fn is_trivially_unpin(&self) -> bool { + fn is_trivially_unpin(self) -> bool { match self.kind() { ty::Int(_) | ty::Uint(_) @@ -750,7 +786,7 @@ impl<'tcx> ty::TyS<'tcx> { | ty::FnDef(..) | ty::Error(_) | ty::FnPtr(_) => true, - ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_unpin), + ty::Tuple(_) => self.tuple_fields().all(|f| Self::is_trivially_unpin(f)), ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_unpin(), ty::Adt(..) | ty::Bound(..) @@ -776,7 +812,7 @@ impl<'tcx> ty::TyS<'tcx> { /// /// Note that this method is used to check eligible types in unions. #[inline] - pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + pub fn needs_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { // Avoid querying in simple cases. match needs_drop_components(self, &tcx.data_layout) { Err(AlwaysRequiresDrop) => true, @@ -809,11 +845,7 @@ impl<'tcx> ty::TyS<'tcx> { /// Note that this method is used to check for change in drop order for /// 2229 drop reorder migration analysis. #[inline] - pub fn has_significant_drop( - &'tcx self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { + pub fn has_significant_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { // Avoid querying in simple cases. match needs_drop_components(self, &tcx.data_layout) { Err(AlwaysRequiresDrop) => true, @@ -858,7 +890,7 @@ impl<'tcx> ty::TyS<'tcx> { /// want to know whether a given call to `PartialEq::eq` will proceed structurally all the way /// down, you will need to use a type visitor. #[inline] - pub fn is_structural_eq_shallow(&'tcx self, tcx: TyCtxt<'tcx>) -> bool { + pub fn is_structural_eq_shallow(self, tcx: TyCtxt<'tcx>) -> bool { match self.kind() { // Look for an impl of both `PartialStructuralEq` and `StructuralEq`. Adt(..) => tcx.has_structural_eq_impls(self), @@ -893,19 +925,6 @@ impl<'tcx> ty::TyS<'tcx> { } } - pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind(), &b.kind()) { - (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => { - if did_a != did_b { - return false; - } - - substs_a.types().zip(substs_b.types()).all(|(a, b)| Self::same_type(a, b)) - } - _ => a == b, - } - } - /// Peel off all reference types in this type until there are none left. /// /// This method is idempotent, i.e. `ty.peel_refs().peel_refs() == ty.peel_refs()`. @@ -916,16 +935,16 @@ impl<'tcx> ty::TyS<'tcx> { /// - `&'a mut u8` -> `u8` /// - `&'a &'b u8` -> `u8` /// - `&'a *const &'b u8 -> *const &'b u8` - pub fn peel_refs(&'tcx self) -> Ty<'tcx> { + pub fn peel_refs(self) -> Ty<'tcx> { let mut ty = self; while let Ref(_, inner_ty, _) = ty.kind() { - ty = inner_ty; + ty = *inner_ty; } ty } - pub fn outer_exclusive_binder(&'tcx self) -> DebruijnIndex { - self.outer_exclusive_binder + pub fn outer_exclusive_binder(self) -> DebruijnIndex { + self.0.outer_exclusive_binder } } @@ -1006,11 +1025,11 @@ pub fn needs_drop_components<'tcx>( ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop), - ty::Slice(ty) => needs_drop_components(ty, target_layout), + ty::Slice(ty) => needs_drop_components(*ty, target_layout), ty::Array(elem_ty, size) => { - match needs_drop_components(elem_ty, target_layout) { + match needs_drop_components(*elem_ty, target_layout) { Ok(v) if v.is_empty() => Ok(v), - res => match size.val.try_to_bits(target_layout.pointer_size) { + res => match size.val().try_to_bits(target_layout.pointer_size) { // Arrays of size zero don't need drop, even if their element // type does. Some(0) => Ok(SmallVec::new()), @@ -1041,6 +1060,42 @@ pub fn needs_drop_components<'tcx>( } } +pub fn is_trivially_const_drop<'tcx>(ty: Ty<'tcx>) -> bool { + match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Never + | ty::Foreign(_) => true, + + ty::Opaque(..) + | ty::Dynamic(..) + | ty::Error(_) + | ty::Bound(..) + | ty::Param(_) + | ty::Placeholder(_) + | ty::Projection(_) + | ty::Infer(_) => false, + + // Not trivial because they have components, and instead of looking inside, + // we'll just perform trait selection. + ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(_) | ty::Adt(..) => false, + + ty::Array(ty, _) | ty::Slice(ty) => is_trivially_const_drop(ty), + + ty::Tuple(tys) => tys.iter().all(|ty| is_trivially_const_drop(ty.expect_ty())), + } +} + // Does the equivalent of // ``` // let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index ba5775fd77..ab70c15160 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -2,7 +2,7 @@ //! WARNING: this does not keep track of the region depth. use crate::ty::subst::{GenericArg, GenericArgKind}; -use crate::ty::{self, TyCtxt}; +use crate::ty::{self, Ty}; use rustc_data_structures::sso::SsoHashSet; use smallvec::{self, SmallVec}; @@ -11,7 +11,6 @@ use smallvec::{self, SmallVec}; type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>; pub struct TypeWalker<'tcx> { - expose_default_const_substs: Option>, stack: TypeWalkerStack<'tcx>, last_subtree: usize, pub visited: SsoHashSet>, @@ -26,13 +25,8 @@ pub struct TypeWalker<'tcx> { /// It maintains a set of visited types and /// skips any types that are already there. impl<'tcx> TypeWalker<'tcx> { - fn new(expose_default_const_substs: Option>, root: GenericArg<'tcx>) -> Self { - Self { - expose_default_const_substs, - stack: smallvec![root], - last_subtree: 1, - visited: SsoHashSet::new(), - } + pub fn new(root: GenericArg<'tcx>) -> Self { + Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() } } /// Skips the subtree corresponding to the last type @@ -61,7 +55,7 @@ impl<'tcx> Iterator for TypeWalker<'tcx> { let next = self.stack.pop()?; self.last_subtree = self.stack.len(); if self.visited.insert(next) { - push_inner(self.expose_default_const_substs, &mut self.stack, next); + push_inner(&mut self.stack, next); debug!("next: stack={:?}", self.stack); return Some(next); } @@ -80,8 +74,8 @@ impl<'tcx> GenericArg<'tcx> { /// Foo> => { Foo>, Bar, isize } /// [isize] => { [isize], isize } /// ``` - pub fn walk(self, tcx: TyCtxt<'tcx>) -> TypeWalker<'tcx> { - TypeWalker::new(Some(tcx), self) + pub fn walk(self) -> TypeWalker<'tcx> { + TypeWalker::new(self) } /// Iterator that walks the immediate children of `self`. Hence @@ -93,21 +87,16 @@ impl<'tcx> GenericArg<'tcx> { /// and skips any types that are already there. pub fn walk_shallow( self, - tcx: TyCtxt<'tcx>, visited: &mut SsoHashSet>, ) -> impl Iterator> { let mut stack = SmallVec::new(); - push_inner(Some(tcx), &mut stack, self); + push_inner(&mut stack, self); stack.retain(|a| visited.insert(*a)); stack.into_iter() } } -impl<'tcx> super::TyS<'tcx> { - pub fn walk_ignoring_default_const_substs(&'tcx self) -> TypeWalker<'tcx> { - TypeWalker::new(None, self.into()) - } - +impl<'tcx> Ty<'tcx> { /// Iterator that walks `self` and any types reachable from /// `self`, in depth-first order. Note that just walks the types /// that appear in `self`, it does not descend into the fields of @@ -118,8 +107,8 @@ impl<'tcx> super::TyS<'tcx> { /// Foo> => { Foo>, Bar, isize } /// [isize] => { [isize], isize } /// ``` - pub fn walk(&'tcx self, tcx: TyCtxt<'tcx>) -> TypeWalker<'tcx> { - TypeWalker::new(Some(tcx), self.into()) + pub fn walk(self) -> TypeWalker<'tcx> { + TypeWalker::new(self.into()) } } @@ -129,11 +118,7 @@ impl<'tcx> super::TyS<'tcx> { /// known to be significant to any code, but it seems like the /// natural order one would expect (basically, the order of the /// types as they are written). -fn push_inner<'tcx>( - expose_default_const_substs: Option>, - stack: &mut TypeWalkerStack<'tcx>, - parent: GenericArg<'tcx>, -) { +fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) { match parent.unpack() { GenericArgKind::Type(parent_ty) => match *parent_ty.kind() { ty::Bool @@ -172,7 +157,7 @@ fn push_inner<'tcx>( stack.extend(obj.iter().rev().flat_map(|predicate| { let (substs, opt_ty) = match predicate.skip_binder() { ty::ExistentialPredicate::Trait(tr) => (tr.substs, None), - ty::ExistentialPredicate::Projection(p) => (p.substs, Some(p.ty)), + ty::ExistentialPredicate::Projection(p) => (p.substs, Some(p.term)), ty::ExistentialPredicate::AutoTrait(_) => // Empty iterator { @@ -180,7 +165,10 @@ fn push_inner<'tcx>( } }; - substs.iter().rev().chain(opt_ty.map(|ty| ty.into())) + substs.iter().rev().chain(opt_ty.map(|term| match term { + ty::Term::Ty(ty) => ty.into(), + ty::Term::Const(ct) => ct.into(), + })) })); } ty::Adt(_, substs) @@ -201,8 +189,8 @@ fn push_inner<'tcx>( }, GenericArgKind::Lifetime(_) => {} GenericArgKind::Const(parent_ct) => { - stack.push(parent_ct.ty.into()); - match parent_ct.val { + stack.push(parent_ct.ty().into()); + match parent_ct.val() { ty::ConstKind::Infer(_) | ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) @@ -211,11 +199,7 @@ fn push_inner<'tcx>( | ty::ConstKind::Error(_) => {} ty::ConstKind::Unevaluated(ct) => { - if let Some(tcx) = expose_default_const_substs { - stack.extend(ct.substs(tcx).iter().rev()); - } else if let Some(substs) = ct.substs_ { - stack.extend(substs.iter().rev()); - } + stack.extend(ct.substs.iter().rev()); } } } diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 5e305ebba2..0c0b0f2bd0 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -1,6 +1,7 @@ //! See docs in build/expr/mod.rs use crate::build::Builder; +use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::CanonicalUserTypeAnnotation; @@ -23,11 +24,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { inferred_ty: ty, }) }); - assert_eq!(literal.ty, ty); + assert_eq!(literal.ty(), ty); Constant { span, user_ty, literal: literal.into() } } - ExprKind::StaticRef { literal, .. } => { - Constant { span, user_ty: None, literal: literal.into() } + ExprKind::StaticRef { alloc_id, ty, .. } => { + let const_val = + ConstValue::Scalar(Scalar::from_pointer(alloc_id.into(), &this.tcx)); + let literal = ConstantKind::Val(const_val, ty); + + Constant { span, user_ty: None, literal } } ExprKind::ConstBlock { value } => { Constant { span: span, user_ty: None, literal: value.into() } 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 c6a34ece24..1e94c41d88 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -217,10 +217,6 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( ty::ClosureKind::FnOnce => {} } - // We won't be building MIR if the closure wasn't local - let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); - let closure_span = tcx.hir().span(closure_hir_id); - let Some((capture_index, capture)) = find_capture_matching_projections( typeck_results, @@ -228,6 +224,7 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( closure_def_id, &from_builder.projection, ) else { + let closure_span = tcx.def_span(closure_def_id); if !enable_precise_capture(tcx, closure_span) { bug!( "No associated capture found for {:?}[{:#?}] even though \ @@ -244,6 +241,8 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( return Err(from_builder); }; + // We won't be building MIR if the closure wasn't local + let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); let closure_ty = typeck_results.node_type(closure_hir_id); let substs = match closure_ty.kind() { @@ -266,7 +265,7 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( // we need to deref it upvar_resolved_place_builder = match capture.info.capture_kind { ty::UpvarCapture::ByRef(_) => upvar_resolved_place_builder.deref(), - ty::UpvarCapture::ByValue(_) => upvar_resolved_place_builder, + ty::UpvarCapture::ByValue => upvar_resolved_place_builder, }; let next_projection = capture.place.projections.len(); @@ -336,10 +335,7 @@ impl<'tcx> PlaceBuilder<'tcx> { } crate fn downcast(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx) -> Self { - self.project(PlaceElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), - variant_index, - )) + self.project(PlaceElem::Downcast(Some(adt_def.variants[variant_index].name), variant_index)) } fn index(self, index: Local) -> Self { @@ -573,7 +569,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ConstBlock { .. } | ExprKind::StaticRef { .. } | ExprKind::InlineAsm { .. } - | ExprKind::LlvmInlineAsm { .. } | ExprKind::Yield { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::Call { .. } => { 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 9a86d465f9..1dc49256a6 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -350,7 +350,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Continue { .. } | ExprKind::Return { .. } | ExprKind::InlineAsm { .. } - | ExprKind::LlvmInlineAsm { .. } | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } => { // these do not have corresponding `Rvalue` variants, diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs index fcda52e558..d31f6ed938 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -67,8 +67,7 @@ impl Category { | ExprKind::Repeat { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } - | ExprKind::ThreadLocalRef(_) - | ExprKind::LlvmInlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::ThreadLocalRef(_) => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } | ExprKind::StaticRef { .. } => { Some(Category::Constant) diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index d9896ff5ac..c706e6ef1d 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -90,17 +90,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; let join_block = this.cfg.start_new_block(); - this.cfg.terminate( - then_blk, - source_info, - TerminatorKind::Goto { target: join_block }, - ); - this.cfg.terminate( - else_blk, - source_info, - TerminatorKind::Goto { target: join_block }, - ); - + this.cfg.goto(then_blk, source_info, join_block); + this.cfg.goto(else_blk, source_info, join_block); join_block.unit() } ExprKind::Let { expr, ref pat } => { @@ -109,8 +100,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.lower_let_expr(block, &this.thir[expr], pat, scope, expr_span) }); - let join_block = this.cfg.start_new_block(); - this.cfg.push_assign_constant( true_block, source_info, @@ -133,6 +122,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, ); + let join_block = this.cfg.start_new_block(); this.cfg.goto(true_block, source_info, join_block); this.cfg.goto(false_block, source_info, join_block); join_block.unit() @@ -358,7 +348,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let place_builder = place_builder.clone(); this.consume_by_copy_or_move( place_builder - .field(n, ty) + .field(n, *ty) .into_place(this.tcx, this.typeck_results), ) } @@ -477,9 +467,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // These cases don't actually need a destination - ExprKind::Assign { .. } - | ExprKind::AssignOp { .. } - | ExprKind::LlvmInlineAsm { .. } => { + ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { unpack!(block = this.stmt_expr(block, expr, None)); this.cfg.push_assign_unit(block, source_info, destination, this.tcx); block.unit() diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 4245535450..7419c5b2f7 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -101,38 +101,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { BreakableTarget::Return, source_info, ), - ExprKind::LlvmInlineAsm { asm, ref outputs, ref inputs } => { - debug!("stmt_expr LlvmInlineAsm block_context.push(SubExpr) : {:?}", expr); - this.block_context.push(BlockFrame::SubExpr); - let outputs = outputs - .into_iter() - .copied() - .map(|output| unpack!(block = this.as_place(block, &this.thir[output]))) - .collect::>() - .into_boxed_slice(); - let inputs = inputs - .into_iter() - .copied() - .map(|input| { - let input = &this.thir[input]; - (input.span, unpack!(block = this.as_local_operand(block, &input))) - }) - .collect::>() - .into_boxed_slice(); - this.cfg.push( - block, - Statement { - source_info, - kind: StatementKind::LlvmInlineAsm(Box::new(LlvmInlineAsm { - asm: asm.clone(), - outputs, - inputs, - })), - }, - ); - this.block_context.pop(); - block.unit() - } _ => { assert!( statement_scope.is_some(), diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index e3a05e01ea..ec8cb30965 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -47,6 +47,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let expr_span = expr.span; match expr.kind { + ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => { + let lhs_then_block = unpack!(this.then_else_break( + block, + &this.thir[lhs], + temp_scope_override, + break_scope, + variable_scope_span, + )); + + let rhs_then_block = unpack!(this.then_else_break( + lhs_then_block, + &this.thir[rhs], + temp_scope_override, + break_scope, + variable_scope_span, + )); + + rhs_then_block.unit() + } ExprKind::Scope { region_scope, lint_level, value } => { let region_scope = (region_scope, this.source_info(expr_span)); this.in_scope(region_scope, lint_level, |this| { @@ -245,7 +264,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // The set of places that we are creating fake borrows of. If there are // no match guards then we don't need any fake borrows, so don't track // them. - let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None }; + let mut fake_borrows = match_has_guard.then(FxHashSet::default); let mut otherwise = None; @@ -583,33 +602,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for binding in &candidate_ref.bindings { let local = self.var_local_id(binding.var_id, OutsideGuard); - if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, - )))) = self.local_decls[local].local_info - { - // `try_upvars_resolved` may fail if it is unable to resolve the given - // `PlaceBuilder` inside a closure. In this case, we don't want to include - // a scrutinee place. `scrutinee_place_builder` will fail for destructured - // assignments. This is because a closure only captures the precise places - // that it will read and as a result a closure may not capture the entire - // tuple/struct and rather have individual places that will be read in the - // final MIR. - // Example: - // ``` - // let foo = (0, 1); - // let c = || { - // let (v1, v2) = foo; - // }; - // ``` - if let Ok(match_pair_resolved) = - initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) - { - let place = - match_pair_resolved.into_place(self.tcx, self.typeck_results); - *match_place = Some(place); - } - } else { + )))) = self.local_decls[local].local_info else { bug!("Let binding to non-user variable.") + }; + // `try_upvars_resolved` may fail if it is unable to resolve the given + // `PlaceBuilder` inside a closure. In this case, we don't want to include + // a scrutinee place. `scrutinee_place_builder` will fail for destructured + // assignments. This is because a closure only captures the precise places + // that it will read and as a result a closure may not capture the entire + // tuple/struct and rather have individual places that will be read in the + // final MIR. + // Example: + // ``` + // let foo = (0, 1); + // let c = || { + // let (v1, v2) = foo; + // }; + // ``` + if let Ok(match_pair_resolved) = + initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + let place = match_pair_resolved.into_place(self.tcx, self.typeck_results); + *match_place = Some(place); } } // All of the subcandidates should bind the same locals, so we @@ -948,13 +964,13 @@ enum TestKind<'tcx> { /// /// For `bool` we always generate two edges, one for `true` and one for /// `false`. - options: FxIndexMap<&'tcx ty::Const<'tcx>, u128>, + options: FxIndexMap, u128>, }, /// Test for equality with value, possibly after an unsizing coercion to /// `ty`, Eq { - value: &'tcx ty::Const<'tcx>, + value: ty::Const<'tcx>, // Integer types are handled by `SwitchInt`, and constants with ADT // types are converted back into patterns, so this can only be `&str`, // `&[T]`, `f32` or `f64`. @@ -1328,23 +1344,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut otherwise = None; for match_pair in match_pairs { - if let PatKind::Or { ref pats } = *match_pair.pattern.kind { - let or_span = match_pair.pattern.span; - let place = match_pair.place; - - first_candidate.visit_leaves(|leaf_candidate| { - self.test_or_pattern( - leaf_candidate, - &mut otherwise, - pats, - or_span, - place.clone(), - fake_borrows, - ); - }); - } else { + let PatKind::Or { ref pats } = &*match_pair.pattern.kind else { bug!("Or-patterns should have been sorted to the end"); - } + }; + let or_span = match_pair.pattern.span; + let place = match_pair.place; + + first_candidate.visit_leaves(|leaf_candidate| { + self.test_or_pattern( + leaf_candidate, + &mut otherwise, + pats, + or_span, + place.clone(), + fake_borrows, + ); + }); } let remainder_start = otherwise.unwrap_or_else(|| self.cfg.start_new_block()); diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 4ce26cc8df..4f9a2c0ce7 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -210,7 +210,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } PatKind::Range(PatRange { lo, hi, end }) => { - let (range, bias) = match *lo.ty.kind() { + let (range, bias) = match *lo.ty().kind() { ty::Char => { (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0) } @@ -228,7 +228,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => (None, 0), }; if let Some((min, max, sz)) = range { - if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) { + if let (Some(lo), Some(hi)) = + (lo.val().try_to_bits(sz), hi.val().try_to_bits(sz)) + { // We want to compare ranges numerically, but the order of the bitwise // representation of signed integers does not match their numeric order. // Thus, to correct the ordering, we need to shift the range of signed diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index a01df2372a..ce848773b1 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -59,8 +59,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, PatKind::Range(range) => { - assert_eq!(range.lo.ty, match_pair.pattern.ty); - assert_eq!(range.hi.ty, match_pair.pattern.ty); + assert_eq!(range.lo.ty(), match_pair.pattern.ty); + assert_eq!(range.hi.ty(), match_pair.pattern.ty); Test { span: match_pair.pattern.span, kind: TestKind::Range(range) } } @@ -86,13 +86,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { test_place: &PlaceBuilder<'tcx>, candidate: &Candidate<'pat, 'tcx>, switch_ty: Ty<'tcx>, - options: &mut FxIndexMap<&'tcx ty::Const<'tcx>, u128>, + options: &mut FxIndexMap, u128>, ) -> bool { - let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) { - Some(match_pair) => match_pair, - _ => { - return false; - } + let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place) else { + return false; }; match *match_pair.pattern.kind { @@ -230,16 +227,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let target_blocks = make_target_blocks(self); let terminator = if *switch_ty.kind() == ty::Bool { assert!(!options.is_empty() && options.len() <= 2); - if let [first_bb, second_bb] = *target_blocks { - let (true_bb, false_bb) = match options[0] { - 1 => (first_bb, second_bb), - 0 => (second_bb, first_bb), - v => span_bug!(test.span, "expected boolean value but got {:?}", v), - }; - TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb) - } else { + let [first_bb, second_bb] = *target_blocks else { bug!("`TestKind::SwitchInt` on `bool` should have two targets") - } + }; + let (true_bb, false_bb) = match options[0] { + 1 => (first_bb, second_bb), + 0 => (second_bb, first_bb), + v => span_bug!(test.span, "expected boolean value but got {:?}", v), + }; + TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb) } else { // The switch may be inexhaustive so we have a catch all block debug_assert_eq!(options.len() + 1, target_blocks.len()); @@ -270,7 +266,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ty, ); } else if let [success, fail] = *make_target_blocks(self) { - assert_eq!(value.ty, ty); + assert_eq!(value.ty(), ty); let expect = self.literal_operand(test.span, value); let val = Operand::Copy(place); self.compare(block, success, fail, source_info, BinOp::Eq, expect, val); @@ -279,7 +275,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - TestKind::Range(PatRange { ref lo, ref hi, ref end }) => { + TestKind::Range(PatRange { lo, hi, ref end }) => { let lower_bound_success = self.cfg.start_new_block(); let target_blocks = make_target_blocks(self); @@ -288,24 +284,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let hi = self.literal_operand(test.span, hi); let val = Operand::Copy(place); - if let [success, fail] = *target_blocks { - self.compare( - block, - lower_bound_success, - fail, - source_info, - BinOp::Le, - lo, - val.clone(), - ); - let op = match *end { - RangeEnd::Included => BinOp::Le, - RangeEnd::Excluded => BinOp::Lt, - }; - self.compare(lower_bound_success, success, fail, source_info, op, val, hi); - } else { + let [success, fail] = *target_blocks else { bug!("`TestKind::Range` should have two target blocks"); - } + }; + self.compare( + block, + lower_bound_success, + fail, + source_info, + BinOp::Le, + lo, + val.clone(), + ); + let op = match *end { + RangeEnd::Included => BinOp::Le, + RangeEnd::Excluded => BinOp::Lt, + }; + self.compare(lower_bound_success, success, fail, source_info, op, val, hi); } TestKind::Len { len, op } => { @@ -320,21 +315,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // expected = let expected = self.push_usize(block, source_info, len); - if let [true_bb, false_bb] = *target_blocks { - // result = actual == expected OR result = actual < expected - // branch based on result - self.compare( - block, - true_bb, - false_bb, - source_info, - op, - Operand::Move(actual), - Operand::Move(expected), - ); - } else { + let [true_bb, false_bb] = *target_blocks else { bug!("`TestKind::Len` should have two target blocks"); - } + }; + // result = actual == expected OR result = actual < expected + // branch based on result + self.compare( + block, + true_bb, + false_bb, + source_info, + op, + Operand::Move(actual), + Operand::Move(expected), + ); } } } @@ -375,7 +369,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block: BasicBlock, make_target_blocks: impl FnOnce(&mut Self) -> Vec, source_info: SourceInfo, - value: &'tcx ty::Const<'tcx>, + value: ty::Const<'tcx>, place: Place<'tcx>, mut ty: Ty<'tcx>, ) { @@ -396,14 +390,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => None, }; let opt_ref_ty = unsize(ty); - let opt_ref_test_ty = unsize(value.ty); + let opt_ref_test_ty = unsize(value.ty()); match (opt_ref_ty, opt_ref_test_ty) { // nothing to do, neither is an array (None, None) => {} (Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => { let tcx = self.tcx; // make both a slice - ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); + ty = tcx.mk_imm_ref(*region, tcx.mk_slice(*elem_ty)); if opt_ref_ty.is_some() { let temp = self.temp(ty, source_info.span); self.cfg.push_assign( @@ -462,16 +456,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); self.diverge_from(block); - if let [success_block, fail_block] = *make_target_blocks(self) { - // check the result - self.cfg.terminate( - eq_block, - source_info, - TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block), - ); - } else { + let [success_block, fail_block] = *make_target_blocks(self) else { bug!("`TestKind::Eq` should have two target blocks") - } + }; + // check the result + self.cfg.terminate( + eq_block, + source_info, + TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block), + ); } /// Given that we are performing `test` against `test_place`, this job @@ -653,7 +646,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = self.tcx; - let test_ty = test.lo.ty; + let test_ty = test.lo.ty(); let lo = compare_const_vals(tcx, test.lo, pat.hi, self.param_env, test_ty)?; let hi = compare_const_vals(tcx, test.hi, pat.lo, self.param_env, test_ty)?; @@ -754,10 +747,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, // we want to create a set of derived match-patterns like // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. - let elem = ProjectionElem::Downcast( - Some(adt_def.variants[variant_index].ident.name), - variant_index, - ); + let elem = + ProjectionElem::Downcast(Some(adt_def.variants[variant_index].name), variant_index); let downcast_place = match_pair.place.project(elem); // `(x as Variant)` let consequent_match_pairs = subpatterns.iter().map(|subpattern| { // e.g., `(x as Variant).0` @@ -773,17 +764,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern) } - fn const_range_contains( - &self, - range: PatRange<'tcx>, - value: &'tcx ty::Const<'tcx>, - ) -> Option { + fn const_range_contains(&self, range: PatRange<'tcx>, value: ty::Const<'tcx>) -> Option { use std::cmp::Ordering::*; let tcx = self.tcx; - let a = compare_const_vals(tcx, range.lo, value, self.param_env, range.lo.ty)?; - let b = compare_const_vals(tcx, value, range.hi, self.param_env, range.lo.ty)?; + let a = compare_const_vals(tcx, range.lo, value, self.param_env, range.lo.ty())?; + let b = compare_const_vals(tcx, value, range.hi, self.param_env, range.lo.ty())?; match (b, range.end) { (Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true), @@ -794,7 +781,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn values_not_contained_in_range( &self, range: PatRange<'tcx>, - options: &FxIndexMap<&'tcx ty::Const<'tcx>, u128>, + options: &FxIndexMap, u128>, ) -> Option { for &val in options.keys() { if self.const_range_contains(range, val)? { @@ -840,7 +827,7 @@ fn trait_method<'tcx>( method_name: Symbol, self_ty: Ty<'tcx>, params: &[GenericArg<'tcx>], -) -> &'tcx ty::Const<'tcx> { +) -> ty::Const<'tcx> { let substs = tcx.mk_substs_trait(self_ty, params); // The unhygienic comparison here is acceptable because this is only diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs index 78047daf0a..fd59144601 100644 --- a/compiler/rustc_mir_build/src/build/misc.rs +++ b/compiler/rustc_mir_build/src/build/misc.rs @@ -25,11 +25,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Convenience function for creating a literal operand, one /// without any user type annotation. - crate fn literal_operand( - &mut self, - span: Span, - literal: &'tcx ty::Const<'tcx>, - ) -> Operand<'tcx> { + crate fn literal_operand(&mut self, span: Span, literal: ty::Const<'tcx>) -> Operand<'tcx> { let literal = literal.into(); let constant = Box::new(Constant { span, user_ty: None, literal }); Operand::Constant(constant) diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index cb94e75997..fb403615e5 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -104,8 +104,8 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ let span_with_body = span_with_body.unwrap_or_else(|| tcx.hir().span(id)); tcx.infer_ctxt().enter(|infcx| { - let body = if let Some(ErrorReported) = typeck_results.tainted_by_errors { - build::construct_error(&infcx, def, id, body_id, body_owner_kind) + let body = if let Some(error_reported) = typeck_results.tainted_by_errors { + build::construct_error(&infcx, def, id, body_id, body_owner_kind, error_reported) } else if body_owner_kind.is_fn_or_closure() { // fetch the fully liberated fn signature (that is, all bound // types/lifetimes replaced) @@ -244,10 +244,10 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ // The exception is `body.user_type_annotations`, which is used unmodified // by borrow checking. debug_assert!( - !(body.local_decls.has_free_regions(tcx) - || body.basic_blocks().has_free_regions(tcx) - || body.var_debug_info.has_free_regions(tcx) - || body.yield_ty().has_free_regions(tcx)), + !(body.local_decls.has_free_regions() + || body.basic_blocks().has_free_regions() + || body.var_debug_info.has_free_regions() + || body.yield_ty().has_free_regions()), "Unexpected free regions in MIR: {:?}", body, ); @@ -715,6 +715,7 @@ fn construct_error<'a, 'tcx>( hir_id: hir::HirId, body_id: hir::BodyId, body_owner_kind: hir::BodyOwnerKind, + err: ErrorReported, ) -> Body<'tcx> { let tcx = infcx.tcx; let span = tcx.hir().span(hir_id); @@ -760,7 +761,6 @@ fn construct_error<'a, 'tcx>( cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); let mut body = Body::new( - tcx, MirSource::item(def.did.to_def_id()), cfg.basic_blocks, source_scopes, @@ -770,6 +770,7 @@ fn construct_error<'a, 'tcx>( vec![], span, generator_kind, + Some(err), ); body.generator.as_mut().map(|gen| gen.yield_ty = Some(ty)); body @@ -849,7 +850,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } Body::new( - self.tcx, MirSource::item(self.def_id), self.cfg.basic_blocks, self.source_scopes, @@ -859,6 +859,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info, self.fn_span, self.generator_kind, + self.typeck_results.tainted_by_errors, ) } @@ -902,7 +903,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut closure_ty = self.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; if let ty::Ref(_, ty, _) = closure_ty.kind() { closure_env_projs.push(ProjectionElem::Deref); - closure_ty = ty; + closure_ty = *ty; } let upvar_substs = match closure_ty.kind() { ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), @@ -930,14 +931,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut projs = closure_env_projs.clone(); projs.push(ProjectionElem::Field(Field::new(i), ty)); match capture { - ty::UpvarCapture::ByValue(_) => {} + ty::UpvarCapture::ByValue => {} ty::UpvarCapture::ByRef(..) => { projs.push(ProjectionElem::Deref); } }; self.var_debug_info.push(VarDebugInfo { - name: sym, + name: *sym, source_info: SourceInfo::outermost(tcx_hir.span(var_id)), value: VarDebugInfoContents::Place(Place { local: ty::CAPTURE_STRUCT_LOCAL, diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index fc46c54c2f..84d6c1d2db 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -498,7 +498,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// if let Some(x) = a && let Some(y) = b && let Some(z) = c { ... } /// - /// there are three possible ways the condition can be false and we may have + /// There are three possible ways the condition can be false and we may have /// to drop `x`, `x` and `y`, or neither depending on which binding fails. /// To handle this correctly we use a `DropTree` in a similar way to a /// `loop` expression and 'break' out on all of the 'else' paths. diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 7940bd1f33..8ca2449cea 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -329,7 +329,6 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | ExprKind::Box { .. } | ExprKind::If { .. } | ExprKind::InlineAsm { .. } - | ExprKind::LlvmInlineAsm { .. } | ExprKind::LogicalOp { .. } | ExprKind::Use { .. } => { // We don't need to save the old value and restore it @@ -377,7 +376,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { self.requires_unsafe(expr.span, DerefOfRawPointer); } } - ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } => { + ExprKind::InlineAsm { .. } => { self.requires_unsafe(expr.span, UseOfInlineAssembly); } ExprKind::Adt(box Adt { diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 38bb00f985..12ea740d48 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -9,6 +9,7 @@ #![feature(once_cell)] #![feature(min_specialization)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index e4c2d2dce6..bccff37987 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -11,9 +11,8 @@ use std::ops::ControlFlow; crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let def_id = body.source.def_id().expect_local(); - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - if let Some(fn_kind) = tcx.hir().get(hir_id).fn_kind() { + if let Some(fn_kind) = tcx.hir().get_by_def_id(def_id).fn_kind() { if let FnKind::Closure = fn_kind { // closures can't recur, so they don't matter. return; @@ -34,6 +33,9 @@ crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) { return; } + if vis.reachable_recursive_calls.is_empty() { + return; + } vis.reachable_recursive_calls.sort(); @@ -64,8 +66,14 @@ struct Search<'mir, 'tcx> { impl<'mir, 'tcx> Search<'mir, 'tcx> { /// Returns `true` if `func` refers to the function we are searching in. - fn is_recursive_call(&self, func: &Operand<'tcx>) -> bool { + fn is_recursive_call(&self, func: &Operand<'tcx>, args: &[Operand<'tcx>]) -> bool { let Search { tcx, body, trait_substs, .. } = *self; + // Resolving function type to a specific instance that is being called is expensive. To + // avoid the cost we check the number of arguments first, which is sufficient to reject + // most of calls as non-recursive. + if args.len() != body.arg_count { + return false; + } let caller = body.source.def_id(); let param_env = tcx.param_env(caller); @@ -139,8 +147,8 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow { // When we examine a node for the last time, remember it if it is a recursive call. let terminator = self.body[bb].terminator(); - if let TerminatorKind::Call { func, .. } = &terminator.kind { - if self.is_recursive_call(func) { + if let TerminatorKind::Call { func, args, .. } = &terminator.kind { + if self.is_recursive_call(func, args) { self.reachable_recursive_calls.push(terminator.source_info.span); } } @@ -149,13 +157,14 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { } fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { + let terminator = self.body[bb].terminator(); + if terminator.unwind() == Some(&Some(target)) && terminator.successors().count() > 1 { + return true; + } // Don't traverse successors of recursive calls or false CFG edges. match self.body[bb].terminator().kind { - TerminatorKind::Call { ref func, .. } => self.is_recursive_call(func), - - TerminatorKind::FalseUnwind { unwind: Some(imaginary_target), .. } - | TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == target, - + TerminatorKind::Call { ref func, ref args, .. } => self.is_recursive_call(func, args), + TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == target, _ => false, } } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 9b54db0d7d..ec2ff3c37a 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -10,7 +10,7 @@ use rustc_target::abi::Size; crate fn lit_to_const<'tcx>( tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>, -) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { +) -> Result, LitToConstError> { let LitToConstInput { lit, ty, neg } = lit_input; let trunc = |n| { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index bdde6b4a35..829dec7480 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -8,7 +8,6 @@ use rustc_middle::hir::place::Place as HirPlace; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::middle::region; -use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{BinOp, BorrowKind, Field, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ @@ -163,9 +162,9 @@ impl<'tcx> Cx<'tcx> { let kind = match expr.kind { // Here comes the interesting stuff: - hir::ExprKind::MethodCall(_, method_span, ref args, fn_span) => { + hir::ExprKind::MethodCall(segment, ref args, fn_span) => { // Rewrite a.b(c) into UFCS form like Trait::b(a, c) - let expr = self.method_callee(expr, method_span, None); + let expr = self.method_callee(expr, segment.ident.span, None); // When we apply adjustments to the receiver, use the span of // the overall method call for better diagnostics. args[0] // is guaranteed to exist, since a method call always has a receiver. @@ -315,7 +314,6 @@ impl<'tcx> Cx<'tcx> { lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs), }, - _ => { let op = bin_op(op.node); ExprKind::Binary { @@ -570,12 +568,6 @@ impl<'tcx> Cx<'tcx> { line_spans: asm.line_spans, }, - hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { - asm: &asm.inner, - outputs: self.mirror_exprs(asm.outputs_exprs), - inputs: self.mirror_exprs(asm.inputs_exprs), - }, - hir::ExprKind::ConstBlock(ref anon_const) => { let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id); @@ -590,7 +582,7 @@ impl<'tcx> Cx<'tcx> { _ => span_bug!(expr.span, "unexpected repeat expr ty: {:?}", ty), }; - ExprKind::Repeat { value: self.mirror_expr(v), count } + ExprKind::Repeat { value: self.mirror_expr(v), count: *count } } hir::ExprKind::Ret(ref v) => { ExprKind::Return { value: v.as_ref().map(|v| self.mirror_expr(v)) } @@ -715,7 +707,7 @@ impl<'tcx> Cx<'tcx> { // in case we are offsetting from a computed discriminant // and not the beginning of discriminants (which is always `0`) let substs = InternalSubsts::identity_for_item(self.tcx(), did); - let lhs = ty::Const { + let lhs = ty::ConstS { val: ty::ConstKind::Unevaluated(ty::Unevaluated::new( ty::WithOptConstParam::unknown(did), substs, @@ -897,7 +889,7 @@ impl<'tcx> Cx<'tcx> { let name = self.tcx.hir().name(hir_id); let val = ty::ConstKind::Param(ty::ParamConst::new(index, name)); ExprKind::Literal { - literal: self.tcx.mk_const(ty::Const { + literal: self.tcx.mk_const(ty::ConstS { val, ty: self.typeck_results().node_type(expr.hir_id), }), @@ -910,7 +902,7 @@ impl<'tcx> Cx<'tcx> { let user_ty = self.user_substs_applied_to_res(expr.hir_id, res); debug!("convert_path_expr: (const) user_ty={:?}", user_ty); ExprKind::Literal { - literal: self.tcx.mk_const(ty::Const { + literal: self.tcx.mk_const(ty::ConstS { val: ty::ConstKind::Unevaluated(ty::Unevaluated::new( ty::WithOptConstParam::unknown(def_id), substs, @@ -950,15 +942,8 @@ impl<'tcx> Cx<'tcx> { let kind = if self.tcx.is_thread_local_static(id) { ExprKind::ThreadLocalRef(id) } else { - let ptr = self.tcx.create_static_alloc(id); - ExprKind::StaticRef { - literal: ty::Const::from_scalar( - self.tcx, - Scalar::from_pointer(ptr.into(), &self.tcx), - ty, - ), - def_id: id, - } + let alloc_id = self.tcx.create_static_alloc(id); + ExprKind::StaticRef { alloc_id, ty, def_id: id } }; ExprKind::Deref { arg: self.thir.exprs.push(Expr { ty, temp_lifetime, span: expr.span, kind }), @@ -1108,9 +1093,9 @@ impl<'tcx> Cx<'tcx> { let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); match upvar_capture { - ty::UpvarCapture::ByValue(_) => captured_place_expr, + ty::UpvarCapture::ByValue => captured_place_expr, ty::UpvarCapture::ByRef(upvar_borrow) => { - let borrow_kind = match upvar_borrow.kind { + let borrow_kind = match upvar_borrow { ty::BorrowKind::ImmBorrow => BorrowKind::Shared, ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique, ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false }, diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 38a4676bd1..a65a3ed31f 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -79,7 +79,7 @@ impl<'tcx> Cx<'tcx> { ty: Ty<'tcx>, sp: Span, neg: bool, - ) -> &'tcx ty::Const<'tcx> { + ) -> ty::Const<'tcx> { trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg); match self.tcx.at(sp).lit_to_const(LitToConstInput { lit, ty, neg }) { 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 7a4fd6ffc4..d357ac6930 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -10,13 +10,14 @@ use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder use rustc_hir as hir; use rustc_hir::def::*; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{HirId, Pat}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_session::lint::builtin::{ BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS, }; use rustc_session::Session; +use rustc_span::source_map::Spanned; use rustc_span::{DesugaringKind, ExpnKind, Span}; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { @@ -54,12 +55,6 @@ struct MatchVisitor<'a, 'p, 'tcx> { } impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { intravisit::walk_expr(self, ex); match &ex.kind { @@ -327,7 +322,7 @@ fn check_for_bindings_named_same_as_variants( if let ty::Adt(edef, _) = pat_ty.kind() { if edef.is_enum() && edef.variants.iter().any(|variant| { - variant.ident == ident && variant.ctor_kind == CtorKind::Const + variant.ident(cx.tcx) == ident && variant.ctor_kind == CtorKind::Const }) { let variant_count = edef.variants.len(); @@ -451,6 +446,10 @@ fn check_let_reachability<'p, 'tcx>( pat: &'p DeconstructedPat<'p, 'tcx>, span: Span, ) { + if is_let_chain(cx.tcx, pat_id) { + return; + } + let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }]; let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty()); @@ -557,7 +556,7 @@ 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) { + if cx.tcx.is_ty_uninhabited_from(cx.module, *sub_ty, cx.param_env) { err.note("references are always considered inhabited"); } } @@ -627,7 +626,7 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( continue; } } - let sp = def.variants[*variant_index].ident.span; + let sp = def.variants[*variant_index].ident(cx.tcx).span; if covered.contains(&sp) { // Don't point at variants that have already been covered due to other patterns to avoid // visual clutter. @@ -770,8 +769,11 @@ pub enum LetSource { fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { let hir = tcx.hir(); + let parent = hir.get_parent_node(pat_id); - match hir.get(parent) { + let parent_node = hir.get(parent); + + match parent_node { hir::Node::Arm(hir::Arm { guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)), .. @@ -786,6 +788,7 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { } _ => {} } + let parent_parent = hir.get_parent_node(parent); let parent_parent_node = hir.get(parent_parent); @@ -798,12 +801,30 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { .. }) = parent_parent_parent_parent_node { - LetSource::WhileLet - } else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) = - parent_parent_node - { - LetSource::IfLet - } else { - LetSource::GenericLet + return LetSource::WhileLet; } + + if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If(..), .. }) = parent_parent_node { + return LetSource::IfLet; + } + + LetSource::GenericLet +} + +// Since this function is called within a let context, it is reasonable to assume that any parent +// `&&` infers a let chain +fn is_let_chain(tcx: TyCtxt<'_>, pat_id: HirId) -> bool { + let hir = tcx.hir(); + let parent = hir.get_parent_node(pat_id); + let parent_parent = hir.get_parent_node(parent); + matches!( + hir.get(parent_parent), + hir::Node::Expr( + hir::Expr { + kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, ..), + .. + }, + .. + ) + ) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index dd16e3cde7..7db71ed598 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -22,7 +22,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn const_to_pat( &self, - cv: &'tcx ty::Const<'tcx>, + cv: ty::Const<'tcx>, id: hir::HirId, span: Span, mir_structural_match_violation: bool, @@ -120,45 +120,39 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option { - traits::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty).map( - |non_sm_ty| { - with_no_trimmed_paths(|| match non_sm_ty { - traits::NonStructuralMatchTy::Adt(adt) => self.adt_derive_msg(adt), - traits::NonStructuralMatchTy::Dynamic => { - "trait objects cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTy::Opaque => { - "opaque types cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTy::Closure => { - "closures cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTy::Generator => { - "generators cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTy::Param => { - bug!("use of a constant whose type is a parameter inside a pattern") - } - traits::NonStructuralMatchTy::Projection => { - bug!("use of a constant whose type is a projection inside a pattern") - } - traits::NonStructuralMatchTy::Foreign => { - bug!("use of a value of a foreign type inside a pattern") - } - }) - }, - ) + traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { + with_no_trimmed_paths(|| match non_sm_ty { + traits::NonStructuralMatchTy::Adt(adt) => self.adt_derive_msg(adt), + traits::NonStructuralMatchTy::Dynamic => { + "trait objects cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTy::Opaque => { + "opaque types cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTy::Closure => { + "closures cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTy::Generator => { + "generators cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTy::Param => { + bug!("use of a constant whose type is a parameter inside a pattern") + } + traits::NonStructuralMatchTy::Projection => { + bug!("use of a constant whose type is a projection inside a pattern") + } + traits::NonStructuralMatchTy::Foreign => { + bug!("use of a value of a foreign type inside a pattern") + } + }) + }) } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { ty.is_structural_eq_shallow(self.infcx.tcx) } - fn to_pat( - &mut self, - cv: &'tcx ty::Const<'tcx>, - mir_structural_match_violation: bool, - ) -> Pat<'tcx> { + fn to_pat(&mut self, cv: ty::Const<'tcx>, mir_structural_match_violation: bool) -> Pat<'tcx> { trace!(self.treat_byte_string_as_slice); // This method is just a wrapper handling a validity check; the heavy lifting is // performed by the recursive `recur` method, which is not meant to be @@ -173,10 +167,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // If we were able to successfully convert the const to some pat, // double-check that all types in the const implement `Structural`. - let structural = self.search_for_structural_match_violation(cv.ty); + let structural = self.search_for_structural_match_violation(cv.ty()); debug!( "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", - cv.ty, structural + cv.ty(), + structural ); // This can occur because const qualification treats all associated constants as @@ -191,7 +186,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } if let Some(msg) = structural { - if !self.type_may_have_partial_eq_impl(cv.ty) { + if !self.type_may_have_partial_eq_impl(cv.ty()) { // span_fatal avoids ICE from resolution of non-existent method (rare case). self.tcx().sess.span_fatal(self.span, &msg); } else if mir_structural_match_violation && !self.saw_const_match_lint.get() { @@ -240,7 +235,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // code at the moment, because types like `for <'a> fn(&'a ())` do // not *yet* implement `PartialEq`. So for now we leave this here. has_impl - || ty.walk(self.tcx()).any(|t| match t.unpack() { + || ty.walk().any(|t| match t.unpack() { ty::subst::GenericArgKind::Lifetime(_) => false, ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(), ty::subst::GenericArgKind::Const(_) => false, @@ -249,7 +244,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { fn field_pats( &self, - vals: impl Iterator>, + vals: impl Iterator>, ) -> Result>, FallbackToConstRef> { vals.enumerate() .map(|(idx, val)| { @@ -262,7 +257,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // Recursive helper for `to_pat`; invoke that (instead of calling this directly). fn recur( &self, - cv: &'tcx ty::Const<'tcx>, + cv: ty::Const<'tcx>, mir_structural_match_violation: bool, ) -> Result, FallbackToConstRef> { let id = self.id; @@ -270,7 +265,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let tcx = self.tcx(); let param_env = self.param_env; - let kind = match cv.ty.kind() { + let kind = match cv.ty().kind() { ty::Float(_) => { if self.include_lint_checks { tcx.struct_span_lint_hir( @@ -294,14 +289,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { PatKind::Wild } ty::Adt(..) - if !self.type_may_have_partial_eq_impl(cv.ty) + if !self.type_may_have_partial_eq_impl(cv.ty()) // FIXME(#73448): Find a way to bring const qualification into parity with // `search_for_structural_match_violation` and then remove this condition. - && self.search_for_structural_match_violation(cv.ty).is_some() => + && self.search_for_structural_match_violation(cv.ty()).is_some() => { // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we // could get `Option`, even though `Option` is annotated with derive. - let msg = self.search_for_structural_match_violation(cv.ty).unwrap(); + let msg = self.search_for_structural_match_violation(cv.ty()).unwrap(); self.saw_const_match_error.set(true); if self.include_lint_checks { tcx.sess.span_err(self.span, &msg); @@ -319,7 +314,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // details. // Backwards compatibility hack because we can't cause hard errors on these // types, so we compare them via `PartialEq::eq` at runtime. - ty::Adt(..) if !self.type_marked_structural(cv.ty) && self.behind_reference.get() => { + ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => { if self.include_lint_checks && !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() @@ -333,7 +328,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let msg = format!( "to use a constant of type `{}` in a pattern, \ `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - cv.ty, cv.ty, + cv.ty(), + cv.ty(), ); lint.build(&msg).emit() }, @@ -344,8 +340,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // `PartialEq::eq` on it. return Err(fallback_to_const_ref(self)); } - ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => { - debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty); + ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => { + debug!( + "adt_def {:?} has !type_marked_structural for cv.ty: {:?}", + adt_def, + cv.ty() + ); let path = tcx.def_path_str(adt_def.did); let msg = format!( "to use a constant of type `{}` in a pattern, \ @@ -380,7 +380,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .destructure_const(param_env.and(cv)) .fields .iter() - .map(|val| self.recur(val, false)) + .map(|val| self.recur(*val, false)) .collect::>()?, slice: None, suffix: Vec::new(), @@ -389,7 +389,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // These are not allowed and will error elsewhere anyway. ty::Dynamic(..) => { self.saw_const_match_error.set(true); - let msg = format!("`{}` cannot be used in patterns", cv.ty); + let msg = format!("`{}` cannot be used in patterns", cv.ty()); if self.include_lint_checks { tcx.sess.span_err(span, &msg); } else { @@ -416,13 +416,13 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .destructure_const(param_env.and(array)) .fields .iter() - .map(|val| self.recur(val, false)) + .map(|val| self.recur(*val, false)) .collect::>()?, slice: None, suffix: vec![], }), span, - ty: pointee_ty, + ty: *pointee_ty, }, }; self.behind_reference.set(old); @@ -442,7 +442,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { .destructure_const(param_env.and(array)) .fields .iter() - .map(|val| self.recur(val, false)) + .map(|val| self.recur(*val, false)) .collect::>()?, slice: None, suffix: vec![], @@ -459,7 +459,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a // reference. This makes the rest of the matching logic simpler as it doesn't have // to figure out how to get a reference again. - ty::Adt(adt_def, _) if !self.type_marked_structural(pointee_ty) => { + ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => { if self.behind_reference.get() { if self.include_lint_checks && !self.saw_const_match_error.get() @@ -546,7 +546,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } _ => { self.saw_const_match_error.set(true); - let msg = format!("`{}` cannot be used in patterns", cv.ty); + let msg = format!("`{}` cannot be used in patterns", cv.ty()); if self.include_lint_checks { tcx.sess.span_err(span, &msg); } else { @@ -562,12 +562,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { && mir_structural_match_violation // FIXME(#73448): Find a way to bring const qualification into parity with // `search_for_structural_match_violation` and then remove this condition. - && self.search_for_structural_match_violation(cv.ty).is_some() + && self.search_for_structural_match_violation(cv.ty()).is_some() { self.saw_const_match_lint.set(true); // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we // could get `Option`, even though `Option` is annotated with derive. - let msg = self.search_for_structural_match_violation(cv.ty).unwrap().replace( + let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace( "in a pattern,", "in a pattern, the constant's initializer must be trivial or", ); @@ -579,6 +579,6 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { ); } - Ok(Pat { span, ty: cv.ty, kind: Box::new(kind) }) + Ok(Pat { span, ty: cv.ty(), kind: Box::new(kind) }) } } 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 368e3957dd..e4d9bd9c23 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -136,12 +136,12 @@ impl IntRange { fn from_const<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - value: &Const<'tcx>, + value: Const<'tcx>, ) -> Option { - if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { - let ty = value.ty; + let ty = value.ty(); + if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) { let val = (|| { - if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val { + if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val() { // For this specific pattern we can skip a lot of effort and go // straight to the result, after doing a bit of checking. (We // could remove this branch and just fall through, which @@ -630,9 +630,9 @@ pub(super) enum Constructor<'tcx> { /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). - FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), + FloatRange(ty::Const<'tcx>, ty::Const<'tcx>, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. - Str(&'tcx ty::Const<'tcx>), + Str(ty::Const<'tcx>), /// Array and slice patterns. Slice(Slice), /// Constants that must not be matched structurally. They are treated as black @@ -815,8 +815,14 @@ impl<'tcx> Constructor<'tcx> { FloatRange(other_from, other_to, other_end), ) => { match ( - compare_const_vals(pcx.cx.tcx, self_to, other_to, pcx.cx.param_env, pcx.ty), - compare_const_vals(pcx.cx.tcx, self_from, other_from, pcx.cx.param_env, pcx.ty), + compare_const_vals(pcx.cx.tcx, *self_to, *other_to, pcx.cx.param_env, pcx.ty), + compare_const_vals( + pcx.cx.tcx, + *self_from, + *other_from, + pcx.cx.param_env, + pcx.ty, + ), ) { (Some(to), Some(from)) => { (from == Ordering::Greater || from == Ordering::Equal) @@ -828,8 +834,13 @@ impl<'tcx> Constructor<'tcx> { } (Str(self_val), Str(other_val)) => { // FIXME: there's probably a more direct way of comparing for equality - match compare_const_vals(pcx.cx.tcx, self_val, other_val, pcx.cx.param_env, pcx.ty) - { + match compare_const_vals( + pcx.cx.tcx, + *self_val, + *other_val, + pcx.cx.param_env, + pcx.ty, + ) { Some(comparison) => comparison == Ordering::Equal, None => false, } @@ -929,7 +940,7 @@ impl<'tcx> SplitWildcard<'tcx> { ty::Bool => smallvec![make_range(0, 1)], ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { let len = len.eval_usize(cx.tcx, cx.param_env) as usize; - if len != 0 && cx.is_uninhabited(sub_ty) { + if len != 0 && cx.is_uninhabited(*sub_ty) { smallvec![] } else { smallvec![Slice(Slice::new(Some(len), VarLen(0, 0)))] @@ -937,7 +948,7 @@ impl<'tcx> SplitWildcard<'tcx> { } // Treat arrays of a constant but unknown length like slices. ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { - let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + let kind = if cx.is_uninhabited(*sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; smallvec![Slice(Slice::new(None, kind))] } ty::Adt(def, substs) if def.is_enum() => { @@ -1368,13 +1379,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) { + if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, *value) { ctor = IntRange(int_range); fields = Fields::empty(); } else { match pat.ty.kind() { ty::Float(_) => { - ctor = FloatRange(value, value, RangeEnd::Included); + ctor = FloatRange(*value, *value, RangeEnd::Included); fields = Fields::empty(); } ty::Ref(_, t, _) if t.is_str() => { @@ -1386,7 +1397,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { // fields. // Note: `t` is `str`, not `&str`. let subpattern = - DeconstructedPat::new(Str(value), Fields::empty(), t, pat.span); + DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span); ctor = Single; fields = Fields::singleton(cx, subpattern) } @@ -1401,11 +1412,11 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } &PatKind::Range(PatRange { lo, hi, end }) => { - let ty = lo.ty; + let ty = lo.ty(); ctor = if let Some(int_range) = IntRange::from_range( cx.tcx, - lo.eval_bits(cx.tcx, cx.param_env, lo.ty), - hi.eval_bits(cx.tcx, cx.param_env, hi.ty), + lo.eval_bits(cx.tcx, cx.param_env, lo.ty()), + hi.eval_bits(cx.tcx, cx.param_env, hi.ty()), ty, &end, ) { @@ -1648,7 +1659,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { }; if let Some(variant) = variant { - write!(f, "{}", variant.ident)?; + write!(f, "{}", variant.name)?; } // Without `cx`, we can't know which field corresponds to which, so we can't diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 55cf807172..ddf39fb824 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -99,7 +99,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); Pat { span: pat.span, - ty: ref_ty, + ty: *ref_ty, kind: Box::new(PatKind::Deref { subpattern: pat }), } }, @@ -121,13 +121,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_range( &mut self, ty: Ty<'tcx>, - lo: &'tcx ty::Const<'tcx>, - hi: &'tcx ty::Const<'tcx>, + lo: ty::Const<'tcx>, + hi: ty::Const<'tcx>, end: RangeEnd, span: Span, ) -> PatKind<'tcx> { - assert_eq!(lo.ty, ty); - assert_eq!(hi.ty, ty); + assert_eq!(lo.ty(), ty); + assert_eq!(hi.ty(), ty); let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env, ty); match (end, cmp) { // `x..y` where `x < y`. @@ -177,16 +177,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { ty: Ty<'tcx>, lo: Option<&PatKind<'tcx>>, hi: Option<&PatKind<'tcx>>, - ) -> Option<(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>)> { + ) -> Option<(ty::Const<'tcx>, ty::Const<'tcx>)> { match (lo, hi) { (Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => { - Some((lo, hi)) + Some((*lo, *hi)) } (Some(PatKind::Constant { value: lo }), None) => { - Some((lo, ty.numeric_max_val(self.tcx)?)) + Some((*lo, ty.numeric_max_val(self.tcx)?)) } (None, Some(PatKind::Constant { value: hi })) => { - Some((ty.numeric_min_val(self.tcx)?, hi)) + Some((ty.numeric_min_val(self.tcx)?, *hi)) } _ => None, } @@ -275,7 +275,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let var_ty = ty; if let ty::BindByReference(_) = bm { if let ty::Ref(_, rty, _) = ty.kind() { - ty = rty; + ty = *rty; } else { bug!("`ref {}` has wrong type {}", ident, ty); } @@ -417,7 +417,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { | DefKind::AssocTy, _, ) - | Res::SelfTy(..) + | Res::SelfTy { .. } | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, _ => { let pattern_error = match res { @@ -493,7 +493,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let const_ = ty::Const::from_value(self.tcx, value, self.typeck_results.node_type(id)); - let pattern = self.const_to_pat(&const_, id, span, mir_structural_match_violation); + let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation); if !is_associated_const { return pattern; @@ -514,7 +514,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { user_ty_span: span, }, }), - ty: const_.ty, + ty: const_.ty(), } } else { pattern @@ -546,7 +546,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Evaluate early like we do in `lower_path`. let value = value.eval(self.tcx, self.param_env); - match value.val { + match value.val() { ConstKind::Param(_) => { self.errors.push(PatternError::ConstParamInPattern(span)); return PatKind::Wild; @@ -744,8 +744,8 @@ impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { crate fn compare_const_vals<'tcx>( tcx: TyCtxt<'tcx>, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Option { @@ -756,13 +756,13 @@ crate fn compare_const_vals<'tcx>( let fallback = || from_bool(a == b); // Use the fallback if any type differs - if a.ty != b.ty || a.ty != ty { + if a.ty() != b.ty() || a.ty() != ty { return fallback(); } // Early return for equal constants (so e.g. references to ZSTs can be compared, even if they // are just integer addresses). - if a.val == b.val { + if a.val() == b.val() { return from_bool(true); } @@ -797,7 +797,7 @@ crate fn compare_const_vals<'tcx>( if let ( ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }), ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }), - ) = (a.val, b.val) + ) = (a.val(), b.val()) { let a_bytes = get_slice_bytes(&tcx, a_val); let b_bytes = get_slice_bytes(&tcx, b_val); diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 11856f6e04..3e1013b035 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; -use std::fmt; +use std::{fmt, iter}; /// The value of an inserted drop flag. #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -329,8 +329,7 @@ where mut succ: BasicBlock, fields: &[(Place<'tcx>, Option)], ) -> Vec { - Some(succ) - .into_iter() + iter::once(succ) .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { succ = self.drop_subpath(place, path, succ, unwind_succ); succ @@ -491,7 +490,7 @@ where if let Some(variant_path) = subpath { let base_place = tcx.mk_place_elem( self.place, - ProjectionElem::Downcast(Some(variant.ident.name), variant_index), + ProjectionElem::Downcast(Some(variant.name), variant_index), ); let fields = self.move_paths_for_fields(base_place, variant_path, &variant, substs); values.push(discr.val); @@ -881,9 +880,9 @@ where ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), ty::Array(ety, size) => { let size = size.try_eval_usize(self.tcx(), self.elaborator.param_env()); - self.open_drop_for_array(ety, size) + self.open_drop_for_array(*ety, size) } - ty::Slice(ety) => self.open_drop_for_array(ety, None), + ty::Slice(ety) => self.open_drop_for_array(*ety, None), _ => bug!("open drop from non-ADT `{:?}`", ty), } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 65c388f812..4871320fdb 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -176,7 +176,6 @@ impl DefUse { // All other contexts are uses... PlaceContext::MutatingUse( MutatingUseContext::AddressOf - | MutatingUseContext::LlvmAsmOutput | MutatingUseContext::Borrow | MutatingUseContext::Drop | MutatingUseContext::Retag, diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 896377f2bc..60cde6546d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -134,11 +134,6 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc | StatementKind::SetDiscriminant { box place, .. } => { trans.gen(place.local); } - StatementKind::LlvmInlineAsm(asm) => { - for place in &*asm.outputs { - trans.gen(place.local); - } - } // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 2e00b4f9a5..26bbc34e78 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -4,7 +4,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use smallvec::{smallvec, SmallVec}; -use std::iter; use std::mem; use super::abs_domain::Lift; @@ -293,16 +292,6 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { StatementKind::FakeRead(box (_, place)) => { self.create_move_path(*place); } - StatementKind::LlvmInlineAsm(ref asm) => { - for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) { - if !kind.is_indirect { - self.gather_init(output.as_ref(), InitKind::Deep); - } - } - for (_, input) in asm.inputs.iter() { - self.gather_operand(input); - } - } StatementKind::StorageLive(_) => {} StatementKind::StorageDead(local) => { self.gather_move(Place::from(*local)); diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index e85d74ef3e..7b000e2e1d 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" doctest = false [dependencies] -itertools = "0.9" +itertools = "0.10" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } tracing = "0.1" rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index a40c4d1c36..fd93744d40 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -104,10 +104,6 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { // safe (at least as emitted during MIR construction) } - StatementKind::LlvmInlineAsm { .. } => self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfInlineAssembly, - ), StatementKind::CopyNonOverlapping(..) => unreachable!(), } self.super_statement(statement, location); @@ -208,7 +204,6 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { MutatingUseContext::Store | MutatingUseContext::Drop | MutatingUseContext::AsmOutput - | MutatingUseContext::LlvmAsmOutput ) ); // If this is just an assignment, determine if the assigned type needs dropping. @@ -398,12 +393,6 @@ struct UnusedUnsafeVisitor<'a> { } impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) { intravisit::walk_block(self, block); diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index e3ff6ad454..5810ce6edc 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -76,10 +76,8 @@ impl<'tcx> MirPass<'tcx> for ConstProp { } let def_id = body.source.def_id().expect_local(); - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - - let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); - let is_assoc_const = tcx.def_kind(def_id.to_def_id()) == DefKind::AssocConst; + let is_fn_like = tcx.hir().get_by_def_id(def_id).fn_kind().is_some(); + let is_assoc_const = tcx.def_kind(def_id) == DefKind::AssocConst; // Only run const prop on functions, methods, closures and associated constants if !is_fn_like && !is_assoc_const { @@ -126,7 +124,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { .predicates_of(def_id.to_def_id()) .predicates .iter() - .filter_map(|(p, _)| if p.is_global(tcx) { Some(*p) } else { None }); + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); if traits::impossible_predicates( tcx, traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(), @@ -138,7 +136,6 @@ impl<'tcx> MirPass<'tcx> for ConstProp { trace!("ConstProp starting for {:?}", def_id); let dummy_body = &Body::new( - tcx, body.source, body.basic_blocks().clone(), body.source_scopes.clone(), @@ -148,6 +145,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { Default::default(), body.span, body.generator_kind(), + body.tainted_by_errors, ); // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold @@ -475,7 +473,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// Returns the value, if any, of evaluating `c`. fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option> { // FIXME we need to revisit this for #67176 - if c.definitely_needs_subst(self.tcx) { + if c.needs_subst() { return None; } @@ -486,18 +484,18 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let err = ConstEvalErr::new(&self.ecx, error, Some(c.span)); if let Some(lint_root) = self.lint_root(source_info) { let lint_only = match c.literal { - ConstantKind::Ty(ct) => match ct.val { + ConstantKind::Ty(ct) => match ct.val() { // Promoteds must lint and not error as the user didn't ask for them ConstKind::Unevaluated(ty::Unevaluated { def: _, - substs_: _, + substs: _, promoted: Some(_), }) => true, // Out of backwards compatibility we cannot report hard errors in unused // generic functions using associated constants of the generic parameters. - _ => c.literal.definitely_needs_subst(*tcx), + _ => c.literal.needs_subst(), }, - ConstantKind::Val(_, ty) => ty.definitely_needs_subst(*tcx), + ConstantKind::Val(_, ty) => ty.needs_subst(), }; if lint_only { // Out of backwards compatibility we cannot report hard errors in unused @@ -728,7 +726,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } // FIXME we need to revisit this for #67176 - if rvalue.definitely_needs_subst(self.tcx) { + if rvalue.needs_subst() { return None; } @@ -803,7 +801,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ) { if let Rvalue::Use(Operand::Constant(c)) = rval { match c.literal { - ConstantKind::Ty(c) if matches!(c.val, ConstKind::Unevaluated(..)) => {} + ConstantKind::Ty(c) if matches!(c.val(), ConstKind::Unevaluated(..)) => {} _ => { trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); return; @@ -843,7 +841,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // Found a value represented as a pair. For now only do const-prop if the type // of `rvalue` is also a tuple with two scalars. // FIXME: enable the general case stated above ^. - let ty = &value.layout.ty; + let ty = value.layout.ty; // Only do it for tuples if let ty::Tuple(substs) = ty.kind() { // Only do it if tuple is also a pair with two scalars @@ -877,7 +875,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { literal: self .ecx .tcx - .mk_const(ty::Const { + .mk_const(ty::ConstS { ty, val: ty::ConstKind::Value(ConstValue::ByRef { alloc, @@ -1035,8 +1033,7 @@ impl Visitor<'_> for CanConstProp { // These could be propagated with a smarter analysis or just some careful thinking about // whether they'd be fine right now. - MutatingUse(MutatingUseContext::LlvmAsmOutput) - | MutatingUse(MutatingUseContext::Yield) + MutatingUse(MutatingUseContext::Yield) | MutatingUse(MutatingUseContext::Drop) | MutatingUse(MutatingUseContext::Retag) // These can't ever be propagated under any scheme, as we can't reason about indirect diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index c61ee6f7e6..62e060c8e0 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -3,7 +3,7 @@ //! //! To enable coverage, include the rustc command line option: //! -//! * `-Z instrument-coverage` +//! * `-C instrument-coverage` //! //! MIR Dump Files, with additional `CoverageGraph` graphviz and `CoverageSpan` spanview //! ------------------------------------------------------------------------------------ @@ -111,6 +111,7 @@ use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use super::spans::CoverageSpan; +use itertools::Itertools; use rustc_middle::mir::create_dump_file; use rustc_middle::mir::generic_graphviz::GraphvizWriter; use rustc_middle::mir::spanview::{self, SpanViewable}; @@ -739,7 +740,6 @@ pub(super) fn dump_coverage_graphviz<'tcx>( ) } }) - .collect::>() .join("\n ") )); } @@ -768,7 +768,6 @@ fn bcb_to_string_sections<'tcx>( .map(|expression| { format!("Intermediate {}", debug_counters.format_counter(expression)) }) - .collect::>() .join("\n"), ); } @@ -783,7 +782,6 @@ fn bcb_to_string_sections<'tcx>( covspan.format(tcx, mir_body) ) }) - .collect::>() .join("\n"), ); } @@ -793,7 +791,6 @@ fn bcb_to_string_sections<'tcx>( dependency_counters .iter() .map(|counter| debug_counters.format_counter(counter)) - .collect::>() .join(" \n"), )); } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index a25402a1ff..57862b6628 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -1,5 +1,6 @@ use super::Error; +use itertools::Itertools; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::dominators::{self, Dominators}; use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; @@ -418,18 +419,11 @@ impl BasicCoverageBlockData { pub fn take_edge_counters( &mut self, ) -> Option> { - self.edge_from_bcbs.take().map_or(None, |m| Some(m.into_iter())) + self.edge_from_bcbs.take().map(|m| m.into_iter()) } pub fn id(&self) -> String { - format!( - "@{}", - self.basic_blocks - .iter() - .map(|bb| bb.index().to_string()) - .collect::>() - .join(ID_SEPARATOR) - ) + format!("@{}", self.basic_blocks.iter().map(|bb| bb.index().to_string()).join(ID_SEPARATOR)) } } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index b009e2fd0e..2bb9f48f9b 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -66,8 +66,8 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { return; } - let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local()); - let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); + let is_fn_like = + tcx.hir().get_by_def_id(mir_source.def_id().expect_local()).fn_kind().is_some(); // Only instrument functions, methods, and closures (not constants since they are evaluated // at compile time by Miri). @@ -579,7 +579,7 @@ fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx let mut hcx = tcx.create_no_span_stable_hashing_context(); let mut stable_hasher = StableHasher::new(); let owner = hir_body.id().hir_id.owner; - let bodies = &tcx.hir_owner_nodes(owner).as_ref().unwrap().bodies; + let bodies = &tcx.hir_owner_nodes(owner).unwrap().bodies; hcx.with_hir_bodies(false, owner, bodies, |hcx| { hir_body.value.hash_stable(hcx, &mut stable_hasher) }); diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 1721fb5cde..da92190452 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -9,7 +9,6 @@ use rustc_span::def_id::DefId; /// A `query` provider for retrieving coverage information injected into MIR. pub(crate) fn provide(providers: &mut Providers) { providers.coverageinfo = |tcx, def_id| coverageinfo(tcx, def_id); - providers.covered_file_name = |tcx, def_id| covered_file_name(tcx, def_id); providers.covered_code_regions = |tcx, def_id| covered_code_regions(tcx, def_id); } @@ -137,30 +136,11 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> coverage_visitor.info } -fn covered_file_name(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - if tcx.is_mir_available(def_id) { - let body = mir_body(tcx, def_id); - for bb_data in body.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() { - if is_inlined(body, statement) { - continue; - } - return Some(code_region.file_name); - } - } - } - } - } - return None; -} - fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> { let body = mir_body(tcx, def_id); body.basic_blocks() .iter() - .map(|data| { + .flat_map(|data| { data.statements.iter().filter_map(|statement| match statement.kind { StatementKind::Coverage(box ref coverage) => { if is_inlined(body, statement) { @@ -172,7 +152,6 @@ fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx Cod _ => None, }) }) - .flatten() .collect() } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index b5356a817f..d1cb2826de 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,6 +1,7 @@ use super::debug::term_type; use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB}; +use itertools::Itertools; use rustc_data_structures::graph::WithNumNodes; use rustc_middle::mir::spanview::source_range_no_file; use rustc_middle::mir::{ @@ -27,7 +28,7 @@ impl CoverageStatement { let stmt = &mir_body[bb].statements[stmt_index]; format!( "{}: @{}[{}]: {:?}", - source_range_no_file(tcx, &span), + source_range_no_file(tcx, span), bb.index(), stmt_index, stmt @@ -37,7 +38,7 @@ impl CoverageStatement { let term = mir_body[bb].terminator(); format!( "{}: @{}.{}: {:?}", - source_range_no_file(tcx, &span), + source_range_no_file(tcx, span), bb.index(), term_type(&term.kind), term.kind @@ -154,7 +155,7 @@ impl CoverageSpan { pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String { format!( "{}\n {}", - source_range_no_file(tcx, &self.span), + source_range_no_file(tcx, self.span), self.format_coverage_statements(tcx, mir_body).replace('\n', "\n "), ) } @@ -169,11 +170,7 @@ impl CoverageSpan { CoverageStatement::Statement(bb, _, index) => (bb, index), CoverageStatement::Terminator(bb, _) => (bb, usize::MAX), }); - sorted_coverage_statements - .iter() - .map(|covstmt| covstmt.format(tcx, mir_body)) - .collect::>() - .join("\n") + sorted_coverage_statements.iter().map(|covstmt| covstmt.format(tcx, mir_body)).join("\n") } /// If the span is part of a macro, returns the macro name symbol. @@ -835,7 +832,6 @@ pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option | StatementKind::CopyNonOverlapping(..) | StatementKind::Assign(_) | StatementKind::SetDiscriminant { .. } - | StatementKind::LlvmInlineAsm(_) | StatementKind::Retag(_, _) | StatementKind::AscribeUserType(_, _) => { Some(statement.source_info.span) diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index b9c79d4cf2..d787443f6a 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -31,29 +31,18 @@ use super::spans; use coverage_test_macros::let_bcb; +use itertools::Itertools; use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::graph::WithSuccessors; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::*; -use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags}; +use rustc_middle::ty::{self, BOOL_TY}; use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP}; // All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`. const TEMP_BLOCK: BasicBlock = BasicBlock::MAX; -fn dummy_ty() -> &'static TyS<'static> { - thread_local! { - static DUMMY_TYS: &'static TyS<'static> = Box::leak(Box::new(TyS::make_for_test( - ty::Bool, - TypeFlags::empty(), - DebruijnIndex::from_usize(0), - ))); - } - - &DUMMY_TYS.with(|tys| *tys) -} - struct MockBlocks<'tcx> { blocks: IndexVec>, dummy_place: Place<'tcx>, @@ -165,7 +154,7 @@ impl<'tcx> MockBlocks<'tcx> { fn switchint(&mut self, some_from_block: Option) -> BasicBlock { let switchint_kind = TerminatorKind::SwitchInt { discr: Operand::Move(Place::from(self.new_temp())), - switch_ty: dummy_ty(), + switch_ty: BOOL_TY, // just a dummy value targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK), }; self.add_block_from(some_from_block, switchint_kind) @@ -232,11 +221,9 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) { mir_body .successors(bb) .map(|successor| { format!(" {:?} -> {:?};", bb, successor) }) - .collect::>() .join("\n") ) }) - .collect::>() .join("\n") ); } @@ -262,11 +249,9 @@ fn print_coverage_graphviz( basic_coverage_blocks .successors(bcb) .map(|successor| { format!(" {:?} -> {:?};", bcb, successor) }) - .collect::>() .join("\n") ) }) - .collect::>() .join("\n") ); } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 2b382468be..237ead591a 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -222,9 +222,11 @@ impl From for UnifyLocal { impl UnifyKey for UnifyLocal { type Value = (); + #[inline] fn index(&self) -> u32 { self.0.as_u32() } + #[inline] fn from_index(u: u32) -> Self { Self(Local::from_u32(u)) } @@ -534,25 +536,6 @@ impl<'a> Conflicts<'a> { // eliminate the resulting self-assignments automatically. StatementKind::Assign(_) => {} - StatementKind::LlvmInlineAsm(asm) => { - // Inputs and outputs must not overlap. - for (_, input) in &*asm.inputs { - if let Some(in_place) = input.place() { - if !in_place.is_indirect() { - for out_place in &*asm.outputs { - if !out_place.is_indirect() && !in_place.is_indirect() { - self.record_local_conflict( - in_place.local, - out_place.local, - "aliasing llvm_asm! operands", - ); - } - } - } - } - } - } - StatementKind::SetDiscriminant { .. } | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index ac88060f0d..ba234dccaa 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -1,12 +1,12 @@ use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; -use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use std::fmt::Debug; use super::simplify::simplify_cfg; /// This pass optimizes something like -/// ```text +/// ```ignore (syntax-highlighting-only) /// let x: Option<()>; /// let y: Option<()>; /// match (x,y) { @@ -15,144 +15,201 @@ use super::simplify::simplify_cfg; /// } /// ``` /// into something like -/// ```text +/// ```ignore (syntax-highlighting-only) /// let x: Option<()>; /// let y: Option<()>; -/// let discriminant_x = // get discriminant of x -/// let discriminant_y = // get discriminant of y -/// if discriminant_x != discriminant_y || discriminant_x == None {1} else {0} +/// let discriminant_x = std::mem::discriminant(x); +/// let discriminant_y = std::mem::discriminant(y); +/// if discriminant_x == discriminant_y { +/// match x { +/// Some(_) => 0, +/// _ => 1, // <---- +/// } // | Actually the same bb +/// } else { // | +/// 1 // <-------------- +/// } +/// ``` +/// +/// Specifically, it looks for instances of control flow like this: +/// ```text +/// +/// ================= +/// | BB1 | +/// |---------------| ============================ +/// | ... | /------> | BBC | +/// |---------------| | |--------------------------| +/// | switchInt(Q) | | | _cl = discriminant(P) | +/// | c | --------/ |--------------------------| +/// | d | -------\ | switchInt(_cl) | +/// | ... | | | c | ---> BBC.2 +/// | otherwise | --\ | /--- | otherwise | +/// ================= | | | ============================ +/// | | | +/// ================= | | | +/// | BBU | <-| | | ============================ +/// |---------------| | \-------> | BBD | +/// |---------------| | | |--------------------------| +/// | unreachable | | | | _dl = discriminant(P) | +/// ================= | | |--------------------------| +/// | | | switchInt(_dl) | +/// ================= | | | d | ---> BBD.2 +/// | BB9 | <--------------- | otherwise | +/// |---------------| ============================ +/// | ... | +/// ================= /// ``` +/// Where the `otherwise` branch on `BB1` is permitted to either go to `BBU` or to `BB9`. In the +/// code: +/// - `BB1` is `parent` and `BBC, BBD` are children +/// - `P` is `child_place` +/// - `child_ty` is the type of `_cl`. +/// - `Q` is `parent_op`. +/// - `parent_ty` is the type of `Q`. +/// - `BB9` is `destination` +/// All this is then transformed into: +/// ```text +/// +/// ======================= +/// | BB1 | +/// |---------------------| ============================ +/// | ... | /------> | BBEq | +/// | _s = discriminant(P)| | |--------------------------| +/// | _t = Ne(Q, _s) | | |--------------------------| +/// |---------------------| | | switchInt(Q) | +/// | switchInt(_t) | | | c | ---> BBC.2 +/// | false | --------/ | d | ---> BBD.2 +/// | otherwise | ---------------- | otherwise | +/// ======================= | ============================ +/// | +/// ================= | +/// | BB9 | <-----------/ +/// |---------------| +/// | ... | +/// ================= +/// ``` +/// +/// This is only correct for some `P`, since `P` is now computed outside the original `switchInt`. +/// The filter on which `P` are allowed (together with discussion of its correctness) is found in +/// `may_hoist`. pub struct EarlyOtherwiseBranch; impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // FIXME(#78496) - sess.opts.debugging_opts.unsound_mir_opts && sess.mir_opt_level() >= 3 + sess.mir_opt_level() >= 2 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("running EarlyOtherwiseBranch on {:?}", body.source); - // we are only interested in this bb if the terminator is a switchInt - let bbs_with_switch = - body.basic_blocks().iter_enumerated().filter(|(_, bb)| is_switch(bb.terminator())); + let mut should_cleanup = false; - let opts_to_apply: Vec> = bbs_with_switch - .flat_map(|(bb_idx, bb)| { - let switch = bb.terminator(); - let helper = Helper { body, tcx }; - let infos = helper.go(bb, switch)?; - Some(OptimizationToApply { infos, basic_block_first_switch: bb_idx }) - }) - .collect(); - - let should_cleanup = !opts_to_apply.is_empty(); + // Also consider newly generated bbs in the same pass + for i in 0..body.basic_blocks().len() { + let bbs = body.basic_blocks(); + let parent = BasicBlock::from_usize(i); + let Some(opt_data) = evaluate_candidate(tcx, body, parent) else { + continue + }; - for opt_to_apply in opts_to_apply { - if !tcx.consider_optimizing(|| format!("EarlyOtherwiseBranch {:?}", &opt_to_apply)) { + if !tcx.consider_optimizing(|| format!("EarlyOtherwiseBranch {:?}", &opt_data)) { break; } - trace!("SUCCESS: found optimization possibility to apply: {:?}", &opt_to_apply); + trace!("SUCCESS: found optimization possibility to apply: {:?}", &opt_data); - let statements_before = - body.basic_blocks()[opt_to_apply.basic_block_first_switch].statements.len(); - let end_of_block_location = Location { - block: opt_to_apply.basic_block_first_switch, - statement_index: statements_before, + should_cleanup = true; + + let TerminatorKind::SwitchInt { + discr: parent_op, + switch_ty: parent_ty, + targets: parent_targets + } = &bbs[parent].terminator().kind else { + unreachable!() + }; + // Always correct since we can only switch on `Copy` types + let parent_op = match parent_op { + Operand::Move(x) => Operand::Copy(*x), + Operand::Copy(x) => Operand::Copy(*x), + Operand::Constant(x) => Operand::Constant(x.clone()), }; + let statements_before = bbs[parent].statements.len(); + let parent_end = Location { block: parent, statement_index: statements_before }; let mut patch = MirPatch::new(body); - // create temp to store second discriminant in - let discr_type = opt_to_apply.infos[0].second_switch_info.discr_ty; - let discr_span = opt_to_apply.infos[0].second_switch_info.discr_source_info.span; - let second_discriminant_temp = patch.new_temp(discr_type, discr_span); + // create temp to store second discriminant in, `_s` in example above + let second_discriminant_temp = + patch.new_temp(opt_data.child_ty, opt_data.child_source.span); - patch.add_statement( - end_of_block_location, - StatementKind::StorageLive(second_discriminant_temp), - ); + patch.add_statement(parent_end, StatementKind::StorageLive(second_discriminant_temp)); // create assignment of discriminant - let place_of_adt_to_get_discriminant_of = - opt_to_apply.infos[0].second_switch_info.place_of_adt_discr_read; patch.add_assign( - end_of_block_location, + parent_end, Place::from(second_discriminant_temp), - Rvalue::Discriminant(place_of_adt_to_get_discriminant_of), + Rvalue::Discriminant(opt_data.child_place), ); - // create temp to store NotEqual comparison between the two discriminants - let not_equal = BinOp::Ne; - let not_equal_res_type = not_equal.ty(tcx, discr_type, discr_type); - let not_equal_temp = patch.new_temp(not_equal_res_type, discr_span); - patch.add_statement(end_of_block_location, StatementKind::StorageLive(not_equal_temp)); - - // create NotEqual comparison between the two discriminants - let first_descriminant_place = - opt_to_apply.infos[0].first_switch_info.discr_used_in_switch; - let not_equal_rvalue = Rvalue::BinaryOp( - not_equal, - Box::new(( - Operand::Copy(Place::from(second_discriminant_temp)), - Operand::Copy(first_descriminant_place), - )), + // create temp to store inequality comparison between the two discriminants, `_t` in + // example above + let nequal = BinOp::Ne; + let comp_res_type = nequal.ty(tcx, *parent_ty, opt_data.child_ty); + let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span); + patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp)); + + // create inequality comparison between the two discriminants + let comp_rvalue = Rvalue::BinaryOp( + nequal, + Box::new((parent_op.clone(), Operand::Move(Place::from(second_discriminant_temp)))), ); patch.add_statement( - end_of_block_location, - StatementKind::Assign(Box::new((Place::from(not_equal_temp), not_equal_rvalue))), + parent_end, + StatementKind::Assign(Box::new((Place::from(comp_temp), comp_rvalue))), ); - let new_targets = opt_to_apply - .infos - .iter() - .flat_map(|x| x.second_switch_info.targets_with_values.iter()) - .cloned(); - - let targets = SwitchTargets::new( - new_targets, - opt_to_apply.infos[0].first_switch_info.otherwise_bb, - ); - - // new block that jumps to the correct discriminant case. This block is switched to if the discriminants are equal - let new_switch_data = BasicBlockData::new(Some(Terminator { - source_info: opt_to_apply.infos[0].second_switch_info.discr_source_info, + let eq_new_targets = parent_targets.iter().map(|(value, child)| { + let TerminatorKind::SwitchInt{ targets, .. } = &bbs[child].terminator().kind else { + unreachable!() + }; + (value, targets.target_for_value(value)) + }); + let eq_targets = SwitchTargets::new(eq_new_targets, opt_data.destination); + + // Create `bbEq` in example above + let eq_switch = BasicBlockData::new(Some(Terminator { + source_info: bbs[parent].terminator().source_info, kind: TerminatorKind::SwitchInt { - // the first and second discriminants are equal, so just pick one - discr: Operand::Copy(first_descriminant_place), - switch_ty: discr_type, - targets, + // switch on the first discriminant, so we can mark the second one as dead + discr: parent_op, + switch_ty: opt_data.child_ty, + targets: eq_targets, }, })); - let new_switch_bb = patch.new_block(new_switch_data); + let eq_bb = patch.new_block(eq_switch); - // switch on the NotEqual. If true, then jump to the `otherwise` case. - // If false, then jump to a basic block that then jumps to the correct disciminant case - let true_case = opt_to_apply.infos[0].first_switch_info.otherwise_bb; - let false_case = new_switch_bb; + // Jump to it on the basis of the inequality comparison + let true_case = opt_data.destination; + let false_case = eq_bb; patch.patch_terminator( - opt_to_apply.basic_block_first_switch, + parent, TerminatorKind::if_( tcx, - Operand::Move(Place::from(not_equal_temp)), + Operand::Move(Place::from(comp_temp)), true_case, false_case, ), ); // generate StorageDead for the second_discriminant_temp not in use anymore - patch.add_statement( - end_of_block_location, - StatementKind::StorageDead(second_discriminant_temp), - ); + patch.add_statement(parent_end, StatementKind::StorageDead(second_discriminant_temp)); - // Generate a StorageDead for not_equal_temp in each of the targets, since we moved it into the switch + // Generate a StorageDead for comp_temp in each of the targets, since we moved it into + // the switch for bb in [false_case, true_case].iter() { patch.add_statement( Location { block: *bb, statement_index: 0 }, - StatementKind::StorageDead(not_equal_temp), + StatementKind::StorageDead(comp_temp), ); } @@ -167,201 +224,177 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { } } -fn is_switch(terminator: &Terminator<'_>) -> bool { - matches!(terminator.kind, TerminatorKind::SwitchInt { .. }) -} - -struct Helper<'a, 'tcx> { - body: &'a Body<'tcx>, - tcx: TyCtxt<'tcx>, -} - -#[derive(Debug, Clone)] -struct SwitchDiscriminantInfo<'tcx> { - /// Type of the discriminant being switched on - discr_ty: Ty<'tcx>, - /// The basic block that the otherwise branch points to - otherwise_bb: BasicBlock, - /// Target along with the value being branched from. Otherwise is not included - targets_with_values: Vec<(u128, BasicBlock)>, - discr_source_info: SourceInfo, - /// The place of the discriminant used in the switch - discr_used_in_switch: Place<'tcx>, - /// The place of the adt that has its discriminant read - place_of_adt_discr_read: Place<'tcx>, - /// The type of the adt that has its discriminant read - type_adt_matched_on: Ty<'tcx>, -} - -#[derive(Debug)] -struct OptimizationToApply<'tcx> { - infos: Vec>, - /// Basic block of the original first switch - basic_block_first_switch: BasicBlock, +/// Returns true if computing the discriminant of `place` may be hoisted out of the branch +fn may_hoist<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, place: Place<'tcx>) -> bool { + for (place, proj) in place.iter_projections() { + match proj { + // Dereferencing in the computation of `place` might cause issues from one of two + // cateogires. First, the referrent might be invalid. We protect against this by + // dereferencing references only (not pointers). Second, the use of a reference may + // invalidate other references that are used later (for aliasing reasons). Consider + // where such an invalidated reference may appear: + // - In `Q`: Not possible since `Q` is used as the operand of a `SwitchInt` and so + // cannot contain referenced data. + // - In `BBU`: Not possible since that block contains only the `unreachable` terminator + // - In `BBC.2, BBD.2`: Not possible, since `discriminant(P)` was computed prior to + // reaching that block in the input to our transformation, and so any data + // invalidated by that computation could not have been used there. + // - In `BB9`: Not possible since control flow might have reached `BB9` via the + // `otherwise` branch in `BBC, BBD` in the input to our transformation, which would + // have invalidated the data when computing `discriminant(P)` + // So dereferencing here is correct. + ProjectionElem::Deref => match place.ty(body.local_decls(), tcx).ty.kind() { + ty::Ref(..) => {} + _ => return false, + }, + // Field projections are always valid + ProjectionElem::Field(..) => {} + // We cannot allow + // downcasts either, since the correctness of the downcast may depend on the parent + // branch being taken. An easy example of this is + // ``` + // Q = discriminant(_3) + // P = (_3 as Variant) + // ``` + // However, checking if the child and parent place are the same and only erroring then + // is not sufficient either, since the `discriminant(_3) == 1` (or whatever) check may + // be replaced by another optimization pass with any other condition that can be proven + // equivalent. + ProjectionElem::Downcast(..) => { + return false; + } + // We cannot allow indexing since the index may be out of bounds. + _ => { + return false; + } + } + } + true } #[derive(Debug)] -struct OptimizationInfo<'tcx> { - /// Info about the first switch and discriminant - first_switch_info: SwitchDiscriminantInfo<'tcx>, - /// Info about the second switch and discriminant - second_switch_info: SwitchDiscriminantInfo<'tcx>, +struct OptimizationData<'tcx> { + destination: BasicBlock, + child_place: Place<'tcx>, + child_ty: Ty<'tcx>, + child_source: SourceInfo, } -impl<'tcx> Helper<'_, 'tcx> { - pub fn go( - &self, - bb: &BasicBlockData<'tcx>, - switch: &Terminator<'tcx>, - ) -> Option>> { - // try to find the statement that defines the discriminant that is used for the switch - let discr = self.find_switch_discriminant_info(bb, switch)?; - - // go through each target, finding a discriminant read, and a switch - let results = discr - .targets_with_values - .iter() - .map(|(value, target)| self.find_discriminant_switch_pairing(&discr, *target, *value)); - - // if the optimization did not apply for one of the targets, then abort - if results.clone().any(|x| x.is_none()) || results.len() == 0 { - trace!("NO: not all of the targets matched the pattern for optimization"); - return None; +fn evaluate_candidate<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + parent: BasicBlock, +) -> Option> { + let bbs = body.basic_blocks(); + let TerminatorKind::SwitchInt { + targets, + switch_ty: parent_ty, + .. + } = &bbs[parent].terminator().kind else { + return None + }; + let parent_dest = { + let poss = targets.otherwise(); + // If the fallthrough on the parent is trivially unreachable, we can let the + // children choose the destination + if bbs[poss].statements.len() == 0 + && bbs[poss].terminator().kind == TerminatorKind::Unreachable + { + None + } else { + Some(poss) } - - Some(results.flatten().collect()) + }; + let Some((_, child)) = targets.iter().next() else { + return None + }; + let child_terminator = &bbs[child].terminator(); + let TerminatorKind::SwitchInt { + switch_ty: child_ty, + targets: child_targets, + .. + } = &child_terminator.kind else { + return None + }; + if child_ty != parent_ty { + return None; + } + let Some(StatementKind::Assign(boxed)) + = &bbs[child].statements.first().map(|x| &x.kind) else { + return None; + }; + let (_, Rvalue::Discriminant(child_place)) = &**boxed else { + return None; + }; + let destination = parent_dest.unwrap_or(child_targets.otherwise()); + + // Verify that the optimization is legal in general + // We can hoist evaluating the child discriminant out of the branch + if !may_hoist(tcx, body, *child_place) { + return None; } - fn find_discriminant_switch_pairing( - &self, - discr_info: &SwitchDiscriminantInfo<'tcx>, - target: BasicBlock, - value: u128, - ) -> Option> { - let bb = &self.body.basic_blocks()[target]; - // find switch - let terminator = bb.terminator(); - if is_switch(terminator) { - let this_bb_discr_info = self.find_switch_discriminant_info(bb, terminator)?; - - // the types of the two adts matched on have to be equalfor this optimization to apply - if discr_info.type_adt_matched_on != this_bb_discr_info.type_adt_matched_on { - trace!( - "NO: types do not match. LHS: {:?}, RHS: {:?}", - discr_info.type_adt_matched_on, - this_bb_discr_info.type_adt_matched_on - ); - return None; - } - - // the otherwise branch of the two switches have to point to the same bb - if discr_info.otherwise_bb != this_bb_discr_info.otherwise_bb { - trace!("NO: otherwise target is not the same"); - return None; - } - - // check that the value being matched on is the same. The - if !this_bb_discr_info.targets_with_values.iter().any(|x| x.0 == value) { - trace!("NO: values being matched on are not the same"); - return None; - } - - // only allow optimization if the left and right of the tuple being matched are the same variants. - // so the following should not optimize - // ```rust - // let x: Option<()>; - // let y: Option<()>; - // match (x,y) { - // (Some(_), None) => {}, - // _ => {} - // } - // ``` - // We check this by seeing that the value of the first discriminant is the only other discriminant value being used as a target in the second switch - if !(this_bb_discr_info.targets_with_values.len() == 1 - && this_bb_discr_info.targets_with_values[0].0 == value) - { - trace!( - "NO: The second switch did not have only 1 target (besides otherwise) that had the same value as the value from the first switch that got us here" - ); - return None; - } - - // when the second place is a projection of the first one, it's not safe to calculate their discriminant values sequentially. - // for example, this should not be optimized: - // - // ```rust - // enum E<'a> { Empty, Some(&'a E<'a>), } - // let Some(Some(_)) = e; - // ``` - // - // ```mir - // bb0: { - // _2 = discriminant(*_1) - // switchInt(_2) -> [...] - // } - // bb1: { - // _3 = discriminant(*(((*_1) as Some).0: &E)) - // switchInt(_3) -> [...] - // } - // ``` - let discr_place = discr_info.place_of_adt_discr_read; - let this_discr_place = this_bb_discr_info.place_of_adt_discr_read; - if discr_place.local == this_discr_place.local - && this_discr_place.projection.starts_with(discr_place.projection) - { - trace!("NO: one target is the projection of another"); - return None; - } - - // if we reach this point, the optimization applies, and we should be able to optimize this case - // store the info that is needed to apply the optimization - - Some(OptimizationInfo { - first_switch_info: discr_info.clone(), - second_switch_info: this_bb_discr_info, - }) - } else { - None + // Verify that the optimization is legal for each branch + for (value, child) in targets.iter() { + if !verify_candidate_branch(&bbs[child], value, *child_place, destination) { + return None; } } + Some(OptimizationData { + destination, + child_place: *child_place, + child_ty: *child_ty, + child_source: child_terminator.source_info, + }) +} - fn find_switch_discriminant_info( - &self, - bb: &BasicBlockData<'tcx>, - switch: &Terminator<'tcx>, - ) -> Option> { - match &switch.kind { - TerminatorKind::SwitchInt { discr, targets, .. } => { - let discr_local = discr.place()?.as_local()?; - // the declaration of the discriminant read. Place of this read is being used in the switch - let discr_decl = &self.body.local_decls()[discr_local]; - let discr_ty = discr_decl.ty; - // the otherwise target lies as the last element - let otherwise_bb = targets.otherwise(); - let targets_with_values = targets.iter().collect(); - - // find the place of the adt where the discriminant is being read from - // assume this is the last statement of the block - let place_of_adt_discr_read = match bb.statements.last()?.kind { - StatementKind::Assign(box (_, Rvalue::Discriminant(adt_place))) => { - Some(adt_place) - } - _ => None, - }?; - - let type_adt_matched_on = place_of_adt_discr_read.ty(self.body, self.tcx).ty; - - Some(SwitchDiscriminantInfo { - discr_used_in_switch: discr.place()?, - discr_ty, - otherwise_bb, - targets_with_values, - discr_source_info: discr_decl.source_info, - place_of_adt_discr_read, - type_adt_matched_on, - }) - } - _ => unreachable!("must only be passed terminator that is a switch"), - } +fn verify_candidate_branch<'tcx>( + branch: &BasicBlockData<'tcx>, + value: u128, + place: Place<'tcx>, + destination: BasicBlock, +) -> bool { + // In order for the optimization to be correct, the branch must... + // ...have exactly one statement + if branch.statements.len() != 1 { + return false; + } + // ...assign the descriminant of `place` in that statement + let StatementKind::Assign(boxed) = &branch.statements[0].kind else { + return false + }; + let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed else { + return false + }; + if *from_place != place { + return false; + } + // ...make that assignment to a local + if discr_place.projection.len() != 0 { + return false; + } + // ...terminate on a `SwitchInt` that invalidates that local + let TerminatorKind::SwitchInt{ discr: switch_op, targets, .. } = &branch.terminator().kind else { + return false + }; + if *switch_op != Operand::Move(*discr_place) { + return false; + } + // ...fall through to `destination` if the switch misses + if destination != targets.otherwise() { + return false; + } + // ...have a branch for value `value` + let mut iter = targets.iter(); + let Some((target_value, _)) = iter.next() else { + return false; + }; + if target_value != value { + return false; + } + // ...and have no more branches + if let Some(_) = iter.next() { + return false; } + return true; } diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index 05834b443d..2ed14b9177 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_middle::mir::visit::Visitor; @@ -5,7 +6,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{ self, subst::{GenericArgKind, Subst, SubstsRef}, - PredicateKind, Ty, TyCtxt, TyS, + PredicateKind, Ty, TyCtxt, }; use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES; use rustc_span::{symbol::sym, Span}; @@ -42,54 +43,28 @@ impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> { } = &terminator.kind { let source_info = *self.body.source_info(location); - // Only handle function calls outside macros - if !source_info.span.from_expansion() { - let func_ty = func.ty(self.body, self.tcx); - if let ty::FnDef(def_id, substs_ref) = *func_ty.kind() { - // Handle calls to `transmute` - if self.tcx.is_diagnostic_item(sym::transmute, def_id) { - let arg_ty = args[0].ty(self.body, self.tcx); - for generic_inner_ty in arg_ty.walk(self.tcx) { - if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() { - if let Some((fn_id, fn_substs)) = - FunctionItemRefChecker::is_fn_ref(inner_ty) - { - let span = self.nth_arg_span(&args, 0); - self.emit_lint(fn_id, fn_substs, source_info, span); - } + let func_ty = func.ty(self.body, self.tcx); + if let ty::FnDef(def_id, substs_ref) = *func_ty.kind() { + // Handle calls to `transmute` + if self.tcx.is_diagnostic_item(sym::transmute, def_id) { + let arg_ty = args[0].ty(self.body, self.tcx); + for generic_inner_ty in arg_ty.walk() { + if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() { + if let Some((fn_id, fn_substs)) = + FunctionItemRefChecker::is_fn_ref(inner_ty) + { + let span = self.nth_arg_span(&args, 0); + self.emit_lint(fn_id, fn_substs, source_info, span); } } - } else { - self.check_bound_args(def_id, substs_ref, &args, source_info); } + } else { + self.check_bound_args(def_id, substs_ref, &args, source_info); } } } self.super_terminator(terminator, location); } - - /// Emits a lint for function references formatted with `fmt::Pointer::fmt` by macros. These - /// cases are handled as operands instead of call terminators to avoid any dependence on - /// unstable, internal formatting details like whether `fmt` is called directly or not. - fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { - let source_info = *self.body.source_info(location); - if source_info.span.from_expansion() { - let op_ty = operand.ty(self.body, self.tcx); - if let ty::FnDef(def_id, substs_ref) = *op_ty.kind() { - if self.tcx.is_diagnostic_item(sym::pointer_trait_fmt, def_id) { - let param_ty = substs_ref.type_at(0); - if let Some((fn_id, fn_substs)) = FunctionItemRefChecker::is_fn_ref(param_ty) { - // The operand's ctxt wouldn't display the lint since it's inside a macro so - // we have to use the callsite's ctxt. - let callsite_ctxt = source_info.span.source_callsite().ctxt(); - let span = source_info.span.with_ctxt(callsite_ctxt); - self.emit_lint(fn_id, fn_substs, source_info, span); - } - } - } - } - self.super_operand(operand, location); - } } impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { @@ -110,16 +85,22 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs(); for (arg_num, arg_def) in arg_defs.iter().enumerate() { // For all types reachable from the argument type in the fn sig - for generic_inner_ty in arg_def.walk(self.tcx) { + for generic_inner_ty in arg_def.walk() { if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() { // If the inner type matches the type bound by `Pointer` - if TyS::same_type(inner_ty, bound_ty) { + if inner_ty == bound_ty { // Do a substitution using the parameters from the callsite let subst_ty = inner_ty.subst(self.tcx, substs_ref); if let Some((fn_id, fn_substs)) = FunctionItemRefChecker::is_fn_ref(subst_ty) { - let span = self.nth_arg_span(args, arg_num); + let mut span = self.nth_arg_span(args, arg_num); + if span.from_expansion() { + // The operand's ctxt wouldn't display the lint since it's inside a macro so + // we have to use the callsite's ctxt. + let callsite_ctxt = span.source_callsite().ctxt(); + span = span.with_ctxt(callsite_ctxt); + } self.emit_lint(fn_id, fn_substs, source_info, span); } } @@ -197,7 +178,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { let ident = self.tcx.item_name(fn_id).to_ident_string(); let ty_params = fn_substs.types().map(|ty| format!("{}", ty)); let const_params = fn_substs.consts().map(|c| format!("{}", c)); - let params = ty_params.chain(const_params).collect::>().join(", "); + let params = ty_params.chain(const_params).join(", "); let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder(); let variadic = if fn_sig.c_variadic() { ", ..." } else { "" }; let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" }; diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index bc9a104e84..05de52458a 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -726,9 +726,13 @@ fn sanitize_witness<'tcx>( saved_locals: &GeneratorSavedLocals, ) { let did = body.source.def_id(); - let allowed_upvars = tcx.erase_regions(upvars); + let param_env = tcx.param_env(did); + + let allowed_upvars = tcx.normalize_erasing_regions(param_env, upvars); let allowed = match witness.kind() { - &ty::GeneratorWitness(s) => tcx.erase_late_bound_regions(s), + &ty::GeneratorWitness(interior_tys) => { + tcx.normalize_erasing_late_bound_regions(param_env, interior_tys) + } _ => { tcx.sess.delay_span_bug( body.span, @@ -738,8 +742,6 @@ fn sanitize_witness<'tcx>( } }; - let param_env = tcx.param_env(did); - for (local, decl) in body.local_decls.iter_enumerated() { // Ignore locals which are internal or not saved between yields. if !saved_locals.contains(local) || decl.internal { @@ -1239,9 +1241,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let yield_ty = if let Some(yield_ty) = body.yield_ty() { - yield_ty - } else { + let Some(yield_ty) = body.yield_ty() else { // This only applies to generators return; }; @@ -1447,9 +1447,6 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { self.check_assigned_place(*lhs, |this| this.visit_rvalue(rhs, location)); } - // FIXME: Does `llvm_asm!` have any aliasing requirements? - StatementKind::LlvmInlineAsm(_) => {} - StatementKind::FakeRead(..) | StatementKind::SetDiscriminant { .. } | StatementKind::StorageLive(_) diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index e1f30fef44..55ce5910c8 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -7,6 +7,7 @@ use rustc_index::vec::Idx; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_span::{hygiene::ExpnKind, ExpnData, Span}; @@ -75,10 +76,18 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { return false; } + let param_env = tcx.param_env_reveal_all_normalized(def_id); + let param_env = rustc_trait_selection::traits::normalize_param_env_or_error( + tcx, + def_id, + param_env, + ObligationCause::misc(body.span, hir_id), + ); + let mut this = Inliner { tcx, - param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()), - codegen_fn_attrs: tcx.codegen_fn_attrs(body.source.def_id()), + param_env, + codegen_fn_attrs: tcx.codegen_fn_attrs(def_id), hir_id, history: Vec::new(), changed: false, @@ -625,7 +634,7 @@ impl<'tcx> Inliner<'tcx> { caller_body.required_consts.extend( callee_body.required_consts.iter().copied().filter(|&ct| { match ct.literal.const_for_ty() { - Some(ct) => matches!(ct.val, ConstKind::Unevaluated(_)), + Some(ct) => matches!(ct.val(), ConstKind::Unevaluated(_)), None => true, } }), diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index 747e760a18..44ded1647f 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -89,7 +89,7 @@ crate fn mir_callgraph_reachable<'tcx>( // 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.definitely_needs_subst(tcx) { + if callee.needs_subst() { continue; } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 638baa0b8d..e7d5bab8fd 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -10,6 +10,7 @@ #![feature(trusted_step)] #![feature(try_blocks)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; @@ -18,11 +19,11 @@ extern crate rustc_middle; use required_consts::RequiredConstsVisitor; use rustc_const_eval::util; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::steal::Steal; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPass, MirPhase, Promoted}; @@ -42,7 +43,8 @@ mod add_retag; mod check_const_item_mutation; mod check_packed_ref; pub mod check_unsafety; -mod cleanup_post_borrowck; +// This pass is public to allow external drivers to perform MIR cleanup +pub mod cleanup_post_borrowck; mod const_debuginfo; mod const_goto; mod const_prop; @@ -64,7 +66,8 @@ mod match_branches; mod multiple_return_terminators; mod normalize_array_len; mod nrvo; -mod remove_false_edges; +// This pass is public to allow external drivers to perform MIR cleanup +pub mod remove_false_edges; mod remove_noop_landing_pads; mod remove_storage_markers; mod remove_uninit_drops; @@ -74,7 +77,8 @@ mod required_consts; mod reveal_all; mod separate_const_switch; mod shim; -mod simplify; +// This pass is public to allow external drivers to perform MIR cleanup +pub mod simplify; mod simplify_branches; mod simplify_comparison_integral; mod simplify_try; @@ -136,8 +140,8 @@ fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// Finds the full set of `DefId`s within the current crate that have /// MIR associated with them. -fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxHashSet { - let mut set = FxHashSet::default(); +fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { + let mut set = FxIndexSet::default(); // All body-owners have MIR associated with them. set.extend(tcx.hir().body_owners()); @@ -146,7 +150,7 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxHashSet { // they don't have a BodyId, so we need to build them separately. struct GatherCtors<'a, 'tcx> { tcx: TyCtxt<'tcx>, - set: &'a mut FxHashSet, + set: &'a mut FxIndexSet, } impl<'tcx> Visitor<'tcx> for GatherCtors<'_, 'tcx> { fn visit_variant_data( @@ -162,10 +166,6 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxHashSet { } intravisit::walk_struct_def(self, v) } - type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } } tcx.hir().visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor()); @@ -252,8 +252,11 @@ fn mir_promoted<'tcx>( // Ensure that we compute the `mir_const_qualif` for constants at // this point, before we steal the mir-const result. // Also this means promotion can rely on all const checks having been done. - let _ = tcx.mir_const_qualif_opt_const_arg(def); + let const_qualifs = tcx.mir_const_qualif_opt_const_arg(def); let mut body = tcx.mir_const(def).steal(); + if let Some(error_reported) = const_qualifs.tainted_by_errors { + body.tainted_by_errors = Some(error_reported); + } let mut required_consts = Vec::new(); let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); @@ -342,7 +345,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) - } } - debug_assert!(!body.has_free_regions(tcx), "Free regions in MIR for CTFE"); + debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE"); body } @@ -358,16 +361,9 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( return tcx.mir_drops_elaborated_and_const_checked(def); } - // (Mir-)Borrowck uses `mir_promoted`, so we have to force it to - // execute before we can steal. - if let Some(param_did) = def.const_param_did { - tcx.ensure().mir_borrowck_const_arg((def.did, param_did)); - } else { - tcx.ensure().mir_borrowck(def.did); - } + let mir_borrowck = tcx.mir_borrowck_opt_const_arg(def); - let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - let is_fn_like = tcx.hir().get(hir_id).fn_kind().is_some(); + let is_fn_like = tcx.hir().get_by_def_id(def.did).fn_kind().is_some(); if is_fn_like { let did = def.did.to_def_id(); let def = ty::WithOptConstParam::unknown(did); @@ -380,6 +376,9 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( let (body, _) = tcx.mir_promoted(def); let mut body = body.steal(); + if let Some(error_reported) = mir_borrowck.tainted_by_errors { + body.tainted_by_errors = Some(error_reported); + } // IMPORTANT pm::run_passes(tcx, &mut body, &[&remove_false_edges::RemoveFalseEdges]); @@ -530,7 +529,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> 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(tcx), "Free regions in optimized MIR"); + debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); body } @@ -545,19 +544,17 @@ fn promoted_mir<'tcx>( return tcx.arena.alloc(IndexVec::new()); } - if let Some(param_did) = def.const_param_did { - tcx.ensure().mir_borrowck_const_arg((def.did, param_did)); - } else { - tcx.ensure().mir_borrowck(def.did); - } - let (_, promoted) = tcx.mir_promoted(def); - let mut promoted = promoted.steal(); + let tainted_by_errors = tcx.mir_borrowck_opt_const_arg(def).tainted_by_errors; + let mut promoted = tcx.mir_promoted(def).1.steal(); for body in &mut promoted { + if let Some(error_reported) = tainted_by_errors { + body.tainted_by_errors = Some(error_reported); + } run_post_borrowck_cleanup_passes(tcx, body); } - debug_assert!(!promoted.has_free_regions(tcx), "Free regions in promoted MIR"); + debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR"); tcx.arena.alloc(promoted) } diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs index e4ac57ac92..cdfd49ef47 100644 --- a/compiler/rustc_mir_transform/src/normalize_array_len.rs +++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs @@ -3,10 +3,11 @@ use crate::MirPass; use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::intern::Interned; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, ReErased, Region, TyCtxt}; const MAX_NUM_BLOCKS: usize = 800; const MAX_NUM_LOCALS: usize = 3000; @@ -211,12 +212,7 @@ fn normalize_array_len_call<'tcx>( let Some(local) = place.as_local() else { return }; match operand { Operand::Copy(place) | Operand::Move(place) => { - let operand_local = - if let Some(local) = place.local_or_deref_local() { - local - } else { - return; - }; + let Some(operand_local) = place.local_or_deref_local() else { return; }; if !interesting_locals.contains(operand_local) { return; } @@ -231,11 +227,15 @@ fn normalize_array_len_call<'tcx>( // current way of patching doesn't allow to work with `mut` ( ty::Ref( - ty::RegionKind::ReErased, + Region(Interned(ReErased, _)), operand_ty, Mutability::Not, ), - ty::Ref(ty::RegionKind::ReErased, cast_ty, Mutability::Not), + ty::Ref( + Region(Interned(ReErased, _)), + cast_ty, + Mutability::Not, + ), ) => { match (operand_ty.kind(), cast_ty.kind()) { // current way of patching doesn't allow to work with `mut` diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 2a73e341f1..77fb092d58 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -50,7 +50,6 @@ impl RemoveNoopLandingPads { StatementKind::Assign { .. } | StatementKind::SetDiscriminant { .. } - | StatementKind::LlvmInlineAsm { .. } | StatementKind::CopyNonOverlapping(..) | StatementKind::Retag { .. } => { return false; diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index 80c87cafea..1c48efd8b4 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -15,7 +15,7 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) { if let Some(ct) = constant.literal.const_for_ty() { - if let ConstKind::Unevaluated(_) = ct.val { + if let ConstKind::Unevaluated(_) = ct.val() { self.required_consts.push(*constant); } } diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs index ee661793a4..8ea550fa12 100644 --- a/compiler/rustc_mir_transform/src/reveal_all.rs +++ b/compiler/rustc_mir_transform/src/reveal_all.rs @@ -39,6 +39,6 @@ impl<'tcx> MutVisitor<'tcx> for RevealAllVisitor<'tcx> { // We have to use `try_normalize_erasing_regions` here, since it's // possible that we visit impossible-to-satisfy where clauses here, // see #91745 - *ty = self.tcx.try_normalize_erasing_regions(self.param_env, *ty).unwrap_or(ty); + *ty = self.tcx.try_normalize_erasing_regions(self.param_env, *ty).unwrap_or(*ty); } } diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs index 612fce71f9..d265720e18 100644 --- a/compiler/rustc_mir_transform/src/separate_const_switch.rs +++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs @@ -239,10 +239,6 @@ fn is_likely_const<'tcx>(mut tracked_place: Place<'tcx>, block: &BasicBlockData< } } - // If inline assembly is found, we probably should - // not try to analyze the code - StatementKind::LlvmInlineAsm(_) => return false, - // These statements have no influence on the place // we are interested in StatementKind::FakeRead(_) @@ -320,10 +316,6 @@ fn find_determining_place<'tcx>( | StatementKind::CopyNonOverlapping(_) | StatementKind::Nop => {} - // If inline assembly is found, we probably should - // not try to analyze the code - StatementKind::LlvmInlineAsm(_) => return None, - // If the discriminant is set, it is always set // as a constant, so the job is already done. // As we are **ignoring projections**, if the place diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 58996dcd67..b8feeb993e 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -68,7 +68,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::DropGlue(def_id, ty) => { // FIXME(#91576): Drop shims for generators aren't subject to the MIR passes at the end // of this function. Is this intentional? - if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(ty::TyS::kind) { + if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(Ty::kind) { let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap(); let body = body.clone().subst(tcx, substs); debug!("make_shim({:?}) = {:?}", instance, body); @@ -137,7 +137,7 @@ fn local_decls_for_sig<'tcx>( span: Span, ) -> IndexVec> { iter::once(LocalDecl::new(sig.output(), span)) - .chain(sig.inputs().iter().map(|ity| LocalDecl::new(ity, span).immutable())) + .chain(sig.inputs().iter().map(|ity| LocalDecl::new(*ity, span).immutable())) .collect() } @@ -171,7 +171,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) let source = MirSource::from_instance(ty::InstanceDef::DropGlue(def_id, ty)); let mut body = - new_body(tcx, source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); + new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); if ty.is_some() { // The first argument (index 0), but add 1 for the return value. @@ -210,7 +210,6 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) } fn new_body<'tcx>( - tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, basic_blocks: IndexVec>, local_decls: IndexVec>, @@ -218,7 +217,6 @@ fn new_body<'tcx>( span: Span, ) -> Body<'tcx> { Body::new( - tcx, source, basic_blocks, IndexVec::from_elem_n( @@ -237,6 +235,8 @@ fn new_body<'tcx>( vec![], span, None, + // FIXME(compiler-errors): is this correct? + None, ) } @@ -362,14 +362,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { self.def_id, self.sig.inputs_and_output[0], )); - new_body( - self.tcx, - source, - self.blocks, - self.local_decls, - self.sig.inputs().len(), - self.span, - ) + new_body(source, self.blocks, self.local_decls, self.sig.inputs().len(), self.span) } fn source_info(&self) -> SourceInfo { @@ -719,14 +712,8 @@ fn build_call_shim<'tcx>( block(&mut blocks, vec![], TerminatorKind::Resume, true); } - let mut body = new_body( - tcx, - MirSource::from_instance(instance), - blocks, - local_decls, - sig.inputs().len(), - span, - ); + let mut body = + new_body(MirSource::from_instance(instance), blocks, local_decls, sig.inputs().len(), span); if let Abi::RustCall = sig.abi { body.spread_arg = Some(Local::new(sig.inputs().len())); @@ -791,7 +778,6 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { let source = MirSource::item(ctor_id); let body = new_body( - tcx, source, IndexVec::from_elem_n(start_block, 1), local_decls, diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 7992124bac..4651e1f4ed 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -303,7 +303,7 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { /// evaluation: `if false { ... }`. /// /// Those statements are bypassed by redirecting paths in the CFG around the -/// `dead blocks`; but with `-Z instrument-coverage`, the dead blocks usually +/// `dead blocks`; but with `-C instrument-coverage`, the dead blocks usually /// include `Coverage` statements representing the Rust source code regions to /// be counted at runtime. Without these `Coverage` statements, the regions are /// lost, and the Rust source code will show no coverage information. @@ -483,8 +483,7 @@ impl UsedLocals { impl<'tcx> Visitor<'tcx> for UsedLocals { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match statement.kind { - StatementKind::LlvmInlineAsm(..) - | StatementKind::CopyNonOverlapping(..) + StatementKind::CopyNonOverlapping(..) | StatementKind::Retag(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs index 7761d4006d..d5507fcc78 100644 --- a/compiler/rustc_mir_transform/src/simplify_try.rs +++ b/compiler/rustc_mir_transform/src/simplify_try.rs @@ -631,10 +631,6 @@ impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> { .filter(|(_, bb)| { // Reaching `unreachable` is UB so assume it doesn't happen. bb.terminator().kind != TerminatorKind::Unreachable - // But `asm!(...)` could abort the program, - // so we cannot assume that the `unreachable` terminator itself is reachable. - // FIXME(Centril): use a normalization pass instead of a check. - || bb.statements.iter().any(|stmt| matches!(stmt.kind, StatementKind::LlvmInlineAsm(..))) }) .peekable(); diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs index 77bc209539..cda9ba9dcc 100644 --- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs @@ -3,8 +3,7 @@ use crate::MirPass; use rustc_data_structures::stable_set::FxHashSet; use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, - TerminatorKind, + BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, TerminatorKind, }; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; @@ -56,7 +55,10 @@ fn variant_discriminants<'tcx>( match &layout.variants { Variants::Single { index } => { let mut res = FxHashSet::default(); - res.insert(index.as_u32() as u128); + res.insert( + ty.discriminant_for_variant(tcx, *index) + .map_or(index.as_u32() as u128, |discr| discr.val), + ); res } Variants::Multiple { variants, .. } => variants @@ -75,16 +77,9 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if body.source.promoted.is_some() { - return; - } - trace!("UninhabitedEnumBranching starting for {:?}", body.source); - let basic_block_count = body.basic_blocks().len(); - - for bb in 0..basic_block_count { - let bb = BasicBlock::from_usize(bb); + for bb in body.basic_blocks().indices() { trace!("processing block {:?}", bb); let Some(discriminant_ty) = get_switched_on_type(&body.basic_blocks()[bb], tcx, body) else { diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index 9e755ab141..f916ca3621 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -23,23 +23,14 @@ impl MirPass<'_> for UnreachablePropagation { for (bb, bb_data) in traversal::postorder(body) { let terminator = bb_data.terminator(); - // HACK: If the block contains any asm statement it is not regarded as unreachable. - // This is a temporary solution that handles possibly diverging asm statements. - // Accompanying testcases: mir-opt/unreachable_asm.rs and mir-opt/unreachable_asm_2.rs - let asm_stmt_in_block = || { - bb_data.statements.iter().any(|stmt: &Statement<'_>| { - matches!(stmt.kind, StatementKind::LlvmInlineAsm(..)) - }) - }; - - if terminator.kind == TerminatorKind::Unreachable && !asm_stmt_in_block() { + if terminator.kind == TerminatorKind::Unreachable { unreachable_blocks.insert(bb); } else { let is_unreachable = |succ: BasicBlock| unreachable_blocks.contains(&succ); let terminator_kind_opt = remove_successors(&terminator.kind, is_unreachable); if let Some(terminator_kind) = terminator_kind_opt { - if terminator_kind == TerminatorKind::Unreachable && !asm_stmt_in_block() { + if terminator_kind == TerminatorKind::Unreachable { unreachable_blocks.insert(bb); } replacements.insert(bb, terminator_kind); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index b70c24b76d..72c1b3fa6e 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -573,7 +573,7 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { let type_length = instance .substs .iter() - .flat_map(|arg| arg.walk(tcx)) + .flat_map(|arg| arg.walk()) .filter(|arg| match arg.unpack() { GenericArgKind::Type(_) | GenericArgKind::Const(_) => true, GenericArgKind::Lifetime(_) => false, @@ -709,7 +709,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let literal = self.monomorphize(constant.literal); let val = match literal { mir::ConstantKind::Val(val, _) => val, - mir::ConstantKind::Ty(ct) => match ct.val { + mir::ConstantKind::Ty(ct) => match ct.val() { ty::ConstKind::Value(val) => val, ty::ConstKind::Unevaluated(ct) => { let param_env = ty::ParamEnv::reveal_all(); @@ -731,13 +731,13 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.visit_ty(literal.ty(), TyContext::Location(location)); } - fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { - debug!("visiting const {:?} @ {:?}", *constant, location); + fn visit_const(&mut self, constant: ty::Const<'tcx>, location: Location) { + debug!("visiting const {:?} @ {:?}", constant, location); - let substituted_constant = self.monomorphize(*constant); + let substituted_constant = self.monomorphize(constant); let param_env = ty::ParamEnv::reveal_all(); - match substituted_constant.val { + match substituted_constant.val() { ty::ConstKind::Value(val) => collect_const_value(self.tcx, val, self.output), ty::ConstKind::Unevaluated(unevaluated) => { match self.tcx.const_eval_resolve(param_env, unevaluated, None) { @@ -807,10 +807,18 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.output.push(create_fn_mono_item(tcx, instance, source)); } } + mir::TerminatorKind::Abort { .. } => { + let instance = Instance::mono( + tcx, + tcx.require_lang_item(LangItem::PanicNoUnwind, Some(source)), + ); + if should_codegen_locally(tcx, &instance) { + self.output.push(create_fn_mono_item(tcx, instance, source)); + } + } mir::TerminatorKind::Goto { .. } | mir::TerminatorKind::SwitchInt { .. } | mir::TerminatorKind::Resume - | mir::TerminatorKind::Abort | mir::TerminatorKind::Return | mir::TerminatorKind::Unreachable => {} mir::TerminatorKind::GeneratorDrop @@ -939,9 +947,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { - let def_id = if let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() { - def_id - } else { + let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; @@ -1034,7 +1040,7 @@ fn find_vtable_types_for_unsizing<'tcx>( match (&source_ty.kind(), &target_ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - ptr_vtable(a, b) + ptr_vtable(*a, *b) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty()) @@ -1310,10 +1316,9 @@ fn create_mono_items_for_default_impls<'tcx>( if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) { let param_env = ty::ParamEnv::reveal_all(); let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref); - let overridden_methods: FxHashSet<_> = - impl_.items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect(); + let overridden_methods = tcx.impl_item_implementor_ids(item.def_id); for method in tcx.provided_trait_methods(trait_ref.def_id) { - if overridden_methods.contains(&method.ident.normalize_to_macros_2_0()) { + if overridden_methods.contains_key(&method.def_id) { continue; } diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 21ac174ba9..bbc65b09ec 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -4,6 +4,7 @@ #![feature(control_flow_enum)] #![feature(let_else)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs index 516c9a9259..681271be7b 100644 --- a/compiler/rustc_monomorphize/src/partitioning/default.rs +++ b/compiler/rustc_monomorphize/src/partitioning/default.rs @@ -303,9 +303,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( // When polymorphization is enabled, methods which do not depend on their generic // parameters, but the self-type of their impl block do will fail to normalize. - if !tcx.sess.opts.debugging_opts.polymorphize - || !instance.definitely_needs_subst(tcx) - { + if !tcx.sess.opts.debugging_opts.polymorphize || !instance.needs_subst() { // This is a method within an impl, find out what the self-type is: let impl_self_ty = tcx.subst_and_normalize_erasing_regions( instance.substs, diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index dc22ffc674..b8684a09fd 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -201,6 +201,38 @@ pub fn partition<'tcx>( partitioner.internalize_symbols(cx, &mut post_inlining); } + let instrument_dead_code = + tcx.sess.instrument_coverage() && !tcx.sess.instrument_coverage_except_unused_functions(); + + if instrument_dead_code { + assert!( + post_inlining.codegen_units.len() > 0, + "There must be at least one CGU that code coverage data can be generated in." + ); + + // Find the smallest CGU that has exported symbols and put the dead + // function stubs in that CGU. We look for exported symbols to increase + // the likelihood the linker won't throw away the dead functions. + // FIXME(#92165): In order to truly resolve this, we need to make sure + // the object file (CGU) containing the dead function stubs is included + // in the final binary. This will probably require forcing these + // function symbols to be included via `-u` or `/include` linker args. + let mut cgus: Vec<_> = post_inlining.codegen_units.iter_mut().collect(); + cgus.sort_by_key(|cgu| cgu.size_estimate()); + + let dead_code_cgu = + if let Some(cgu) = cgus.into_iter().rev().find(|cgu| { + cgu.items().iter().any(|(_, (linkage, _))| *linkage == Linkage::External) + }) { + cgu + } else { + // If there are no CGUs that have externally linked items, + // then we just pick the first CGU as a fallback. + &mut post_inlining.codegen_units[0] + }; + dead_code_cgu.make_code_coverage_dead_code_cgu(); + } + // Finally, sort by codegen unit name, so that we get deterministic results. let PostInliningPartitioning { codegen_units: mut result, diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index 595080619d..48b6951f10 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -267,7 +267,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { self.super_local_decl(local, local_decl); } - fn visit_const(&mut self, c: &&'tcx Const<'tcx>, _: Location) { + fn visit_const(&mut self, c: Const<'tcx>, _: Location) { c.visit_with(self); } @@ -277,22 +277,19 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { } impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } #[instrument(level = "debug", skip(self))] - fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow { - if !c.potentially_has_param_types_or_consts() { + fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow { + if !c.has_param_types_or_consts() { return ControlFlow::CONTINUE; } - match c.val { + match c.val() { ty::ConstKind::Param(param) => { debug!(?param); self.unused_parameters.clear(param.index); ControlFlow::CONTINUE } - ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted: Some(p)}) + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted: Some(p)}) // Avoid considering `T` unused when constants are of the form: // `>::foo::promoted[p]` if self.def_id == def.did && !self.tcx.generics_of(def.did).has_self => @@ -306,7 +303,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { ty::ConstKind::Unevaluated(uv) if matches!(self.tcx.def_kind(uv.def.did), DefKind::AnonConst | DefKind::InlineConst) => { - self.visit_child_body(uv.def.did, uv.substs(self.tcx)); + self.visit_child_body(uv.def.did, uv.substs); ControlFlow::CONTINUE } _ => c.super_visit_with(self), @@ -315,7 +312,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - if !ty.potentially_has_param_types_or_consts() { + if !ty.has_param_types_or_consts() { return ControlFlow::CONTINUE; } @@ -343,25 +340,20 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { } /// Visitor used to check if a generic parameter is used. -struct HasUsedGenericParams<'a, 'tcx> { - tcx: TyCtxt<'tcx>, +struct HasUsedGenericParams<'a> { unused_parameters: &'a FiniteBitSet, } -impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a, 'tcx> { +impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> { type BreakTy = (); - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - #[instrument(level = "debug", skip(self))] - fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow { - if !c.potentially_has_param_types_or_consts() { + fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow { + if !c.has_param_types_or_consts() { return ControlFlow::CONTINUE; } - match c.val { + match c.val() { ty::ConstKind::Param(param) => { if self.unused_parameters.contains(param.index).unwrap_or(false) { ControlFlow::CONTINUE @@ -375,7 +367,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - if !ty.potentially_has_param_types_or_consts() { + if !ty.has_param_types_or_consts() { return ControlFlow::CONTINUE; } diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index 6084cdda22..04baa01832 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs @@ -8,13 +8,11 @@ use std::io::prelude::*; /// During the same compile all closures dump the information in the same file /// "closure_profile_XXXXX.csv", which is created in the directory where the compiler is invoked. crate fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: Instance<'tcx>) { - let mut file = if let Ok(file) = OpenOptions::new() + let Ok(mut file) = OpenOptions::new() .create(true) .append(true) .open(&format!("closure_profile_{}.csv", std::process::id())) - { - file - } else { + else { eprintln!("Cound't open file for writing closure profile"); return; }; @@ -49,8 +47,7 @@ crate fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: Instanc .map(|l| format!("{:?}", l.size.bytes())) .unwrap_or_else(|e| format!("Failed {:?}", e)); - let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); - let closure_span = tcx.hir().span(closure_hir_id); + let closure_span = tcx.def_span(closure_def_id); let src_file = tcx.sess.source_map().span_to_filename(closure_span); let line_nos = tcx .sess diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 1a620968d5..4cdd83c0ac 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -158,9 +158,7 @@ impl<'a> StringReader<'a> { Some(match token { rustc_lexer::TokenKind::LineComment { doc_style } => { // Skip non-doc comments - let doc_style = if let Some(doc_style) = doc_style { - doc_style - } else { + let Some(doc_style) = doc_style else { self.lint_unicode_text_flow(start); return None; }; @@ -185,9 +183,7 @@ impl<'a> StringReader<'a> { } // Skip non-doc comments - let doc_style = if let Some(doc_style) = doc_style { - doc_style - } else { + let Some(doc_style) = doc_style else { self.lint_unicode_text_flow(start); return None; }; diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 7f68112a42..a41956c58f 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -185,6 +185,15 @@ pub(crate) fn emit_unescape_error( version control settings", ); } else { + if !mode.is_bytes() { + diag.span_suggestion( + span_with_quotes, + "if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal", + format!("r\"{}\"", lit), + Applicability::MaybeIncorrect, + ); + } + diag.help( "for more information, visit \ ", diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 2b1b2f3fce..eb0d1a12c7 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -4,6 +4,7 @@ #![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(box_patterns)] +#![feature(let_else)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9677e7642b..3d382e598f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,5 +1,5 @@ use super::pat::Expected; -use super::ty::AllowPlus; +use super::ty::{AllowPlus, RecoverQuestionMark}; use super::{ BlockMode, Parser, PathStyle, RecoverColon, RecoverComma, Restrictions, SemiColonMode, SeqSep, TokenExpectType, TokenType, @@ -27,7 +27,7 @@ use std::mem::take; use tracing::{debug, trace}; const TURBOFISH_SUGGESTION_STR: &str = - "use `::<...>` instead of `<...>` to specify type or const arguments"; + "use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments"; /// Creates a placeholder argument. pub(super) fn dummy_arg(ident: Ident) -> Param { @@ -192,10 +192,10 @@ impl<'a> Parser<'a> { if ident.is_raw_guess() && self.look_ahead(1, |t| valid_follow.contains(&t.kind)) => { - err.span_suggestion( - ident.span, - "you can escape reserved keywords to use them as identifiers", - format!("r#{}", ident.name), + err.span_suggestion_verbose( + ident.span.shrink_to_lo(), + &format!("escape `{}` to use it as an identifier", ident.name), + "r#".to_owned(), Applicability::MaybeIncorrect, ); } @@ -550,8 +550,8 @@ impl<'a> Parser<'a> { /// a diagnostic to suggest removing them. /// /// ```ignore (diagnostic) - /// let _ = vec![1, 2, 3].into_iter().collect::>>>(); - /// ^^ help: remove extra angle brackets + /// let _ = [1, 2, 3].into_iter().collect::>>>(); + /// ^^ help: remove extra angle brackets /// ``` /// /// If `true` is returned, then trailing brackets were recovered, tokens were consumed @@ -731,20 +731,21 @@ impl<'a> Parser<'a> { match x { Ok((_, _, false)) => { if self.eat(&token::Gt) { + e.span_suggestion_verbose( + binop.span.shrink_to_lo(), + TURBOFISH_SUGGESTION_STR, + "::".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(); match self.parse_expr() { Ok(_) => { - e.span_suggestion_verbose( - binop.span.shrink_to_lo(), - TURBOFISH_SUGGESTION_STR, - "::".to_string(), - Applicability::MaybeIncorrect, - ); - e.emit(); *expr = self.mk_expr_err(expr.span.to(self.prev_token.span)); return Ok(()); } Err(mut err) => { + *expr = self.mk_expr_err(expr.span); err.cancel(); } } @@ -1032,6 +1033,34 @@ impl<'a> Parser<'a> { } } + /// Swift lets users write `Ty?` to mean `Option`. Parse the construct and recover from it. + pub(super) fn maybe_recover_from_question_mark( + &mut self, + ty: P, + recover_question_mark: RecoverQuestionMark, + ) -> P { + if let RecoverQuestionMark::No = recover_question_mark { + return ty; + } + if self.token == token::Question { + self.bump(); + self.struct_span_err(self.prev_token.span, "invalid `?` in type") + .span_label(self.prev_token.span, "`?` is only allowed on expressions, not types") + .multipart_suggestion( + "if you meant to express that the type might not contain a value, use the `Option` wrapper type", + vec![ + (ty.span.shrink_to_lo(), "Option<".to_string()), + (self.prev_token.span, ">".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + self.mk_ty(ty.span.to(self.prev_token.span), TyKind::Err) + } else { + ty + } + } + pub(super) fn maybe_recover_from_bad_type_plus( &mut self, allow_plus: AllowPlus, @@ -2127,7 +2156,7 @@ impl<'a> Parser<'a> { | PatKind::TupleStruct(qself @ None, path, _) | PatKind::Path(qself @ None, path) => match &first_pat.kind { PatKind::Ident(_, ident, _) => { - path.segments.insert(0, PathSegment::from_ident(ident.clone())); + path.segments.insert(0, PathSegment::from_ident(*ident)); path.span = new_span; show_sugg = true; first_pat = pat; @@ -2154,8 +2183,8 @@ impl<'a> Parser<'a> { Path { span: new_span, segments: vec![ - PathSegment::from_ident(old_ident.clone()), - PathSegment::from_ident(ident.clone()), + PathSegment::from_ident(*old_ident), + PathSegment::from_ident(*ident), ], tokens: None, }, @@ -2165,7 +2194,7 @@ impl<'a> Parser<'a> { } PatKind::Path(old_qself, old_path) => { let mut segments = old_path.segments.clone(); - segments.push(PathSegment::from_ident(ident.clone())); + segments.push(PathSegment::from_ident(*ident)); let path = PatKind::Path( old_qself.clone(), Path { span: new_span, segments, tokens: None }, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f706a98a4f..e9aa4adcaf 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -682,7 +682,7 @@ impl<'a> Parser<'a> { // Save the state of the parser before parsing type normally, in case there is a // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); - let cast_expr = match self.parse_ty_no_plus() { + let cast_expr = match self.parse_as_cast_ty() { Ok(rhs) => mk_expr(self, lhs, rhs), Err(mut type_err) => { // Rewind to before attempting to parse the type with generics, to recover @@ -808,7 +808,7 @@ impl<'a> Parser<'a> { "casts cannot be followed by {}", match with_postfix.kind { ExprKind::Index(_, _) => "indexing", - ExprKind::Try(_) => "?", + ExprKind::Try(_) => "`?`", ExprKind::Field(_, _) => "a field access", ExprKind::MethodCall(_, _, _) => "a method call", ExprKind::Call(_, _) => "a function call", @@ -1443,7 +1443,7 @@ impl<'a> Parser<'a> { &mut self, label: Label, attrs: AttrVec, - consume_colon: bool, + mut consume_colon: bool, ) -> PResult<'a, P> { let lo = label.ident.span; let label = Some(label); @@ -1456,6 +1456,12 @@ impl<'a> Parser<'a> { self.parse_loop_expr(label, lo, attrs) } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() { self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs) + } else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) { + // We're probably inside of a `Path<'a>` that needs a turbofish + let msg = "expected `while`, `for`, `loop` or `{` after a label"; + self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); + consume_colon = false; + Ok(self.mk_expr_err(lo)) } else { let msg = "expected `while`, `for`, `loop` or `{` after a label"; self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); @@ -1694,6 +1700,19 @@ impl<'a> Parser<'a> { s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) } + // Try to lowercase the prefix if it's a valid base prefix. + fn fix_base_capitalisation(s: &str) -> Option { + if let Some(stripped) = s.strip_prefix('B') { + Some(format!("0b{stripped}")) + } else if let Some(stripped) = s.strip_prefix('O') { + Some(format!("0o{stripped}")) + } else if let Some(stripped) = s.strip_prefix('X') { + Some(format!("0x{stripped}")) + } else { + None + } + } + let token::Lit { kind, suffix, .. } = lit; match err { // `NotLiteral` is not an error by itself, so we don't report @@ -1718,6 +1737,18 @@ impl<'a> Parser<'a> { self.struct_span_err(span, &msg) .help("valid widths are 8, 16, 32, 64 and 128") .emit(); + } else if let Some(fixed) = fix_base_capitalisation(suf) { + let msg = "invalid base prefix for number literal"; + + self.struct_span_err(span, &msg) + .note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase") + .span_suggestion( + span, + "try making the prefix lowercase", + fixed, + Applicability::MaybeIncorrect, + ) + .emit(); } else { let msg = format!("invalid suffix `{}` for number literal", suf); self.struct_span_err(span, &msg) @@ -2377,6 +2408,17 @@ impl<'a> Parser<'a> { } pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { + fn check_let_expr(expr: &Expr) -> (bool, bool) { + match expr.kind { + ExprKind::Binary(_, ref lhs, ref rhs) => { + let lhs_rslt = check_let_expr(lhs); + let rhs_rslt = check_let_expr(rhs); + (lhs_rslt.0 || rhs_rslt.0, false) + } + ExprKind::Let(..) => (true, true), + _ => (false, true), + } + } let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; @@ -2384,9 +2426,12 @@ impl<'a> Parser<'a> { let guard = if this.eat_keyword(kw::If) { let if_span = this.prev_token.span; let cond = this.parse_expr()?; - if let ExprKind::Let(..) = cond.kind { - // Remove the last feature gating of a `let` expression since it's stable. - this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond); + if has_let_expr { + if does_not_have_bin_op { + // Remove the last feature gating of a `let` expression since it's stable. + this.sess.gated_spans.ungate_last(sym::let_chains, cond.span); + } let span = if_span.to(cond.span); this.sess.gated_spans.gate(sym::if_let_guard, span); } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 419ea9cced..4b57aa1f24 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -4,7 +4,7 @@ use rustc_ast::token; use rustc_ast::{ self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause, }; -use rustc_errors::PResult; +use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::kw; impl<'a> Parser<'a> { @@ -256,7 +256,21 @@ impl<'a> Parser<'a> { break; } - if !self.eat(&token::Comma) { + let prev_token = self.prev_token.span; + let ate_comma = self.eat(&token::Comma); + + if self.eat_keyword_noexpect(kw::Where) { + let msg = "cannot define duplicate `where` clauses on an item"; + let mut err = self.struct_span_err(self.token.span, msg); + err.span_label(lo, "previous `where` clause starts here"); + err.span_suggestion_verbose( + prev_token.shrink_to_hi().to(self.prev_token.span), + "consider joining the two `where` clauses into one", + ",".to_owned(), + Applicability::MaybeIncorrect, + ); + err.emit(); + } else if !ate_comma { break; } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index ade441b0e7..93f5d79c0d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -423,7 +423,7 @@ impl<'a> Parser<'a> { // Maybe the user misspelled `macro_rules` (issue #91227) if self.token.is_ident() && path.segments.len() == 1 - && lev_distance("macro_rules", &path.segments[0].ident.to_string()) <= 3 + && lev_distance("macro_rules", &path.segments[0].ident.to_string(), 3).is_some() { err.span_suggestion( path.span, @@ -1221,7 +1221,7 @@ impl<'a> Parser<'a> { let struct_def = if this.check(&token::OpenDelim(token::Brace)) { // Parse a struct variant. - let (fields, recovered) = this.parse_record_struct_body("struct")?; + let (fields, recovered) = this.parse_record_struct_body("struct", false)?; VariantData::Struct(fields, recovered) } else if this.check(&token::OpenDelim(token::Paren)) { VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID) @@ -1275,7 +1275,8 @@ impl<'a> Parser<'a> { VariantData::Unit(DUMMY_NODE_ID) } else { // If we see: `struct Foo where T: Copy { ... }` - let (fields, recovered) = self.parse_record_struct_body("struct")?; + let (fields, recovered) = + self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?; VariantData::Struct(fields, recovered) } // No `where` so: `struct Foo;` @@ -1283,7 +1284,8 @@ impl<'a> Parser<'a> { VariantData::Unit(DUMMY_NODE_ID) // Record-style struct definition } else if self.token == token::OpenDelim(token::Brace) { - let (fields, recovered) = self.parse_record_struct_body("struct")?; + let (fields, recovered) = + self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?; VariantData::Struct(fields, recovered) // Tuple-style struct definition with optional where-clause. } else if self.token == token::OpenDelim(token::Paren) { @@ -1313,10 +1315,12 @@ impl<'a> Parser<'a> { let vdata = if self.token.is_keyword(kw::Where) { generics.where_clause = self.parse_where_clause()?; - let (fields, recovered) = self.parse_record_struct_body("union")?; + let (fields, recovered) = + self.parse_record_struct_body("union", generics.where_clause.has_where_token)?; VariantData::Struct(fields, recovered) } else if self.token == token::OpenDelim(token::Brace) { - let (fields, recovered) = self.parse_record_struct_body("union")?; + let (fields, recovered) = + self.parse_record_struct_body("union", generics.where_clause.has_where_token)?; VariantData::Struct(fields, recovered) } else { let token_str = super::token_descr(&self.token); @@ -1332,6 +1336,7 @@ impl<'a> Parser<'a> { fn parse_record_struct_body( &mut self, adt_ty: &str, + parsed_where: bool, ) -> PResult<'a, (Vec, /* recovered */ bool)> { let mut fields = Vec::new(); let mut recovered = false; @@ -1353,9 +1358,19 @@ impl<'a> Parser<'a> { self.eat(&token::CloseDelim(token::Brace)); } else { let token_str = super::token_descr(&self.token); - let msg = &format!("expected `where`, or `{{` after struct name, found {}", token_str); + let msg = &format!( + "expected {}`{{` after struct name, found {}", + if parsed_where { "" } else { "`where`, or " }, + token_str + ); let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected `where`, or `{` after struct name"); + err.span_label( + self.token.span, + format!( + "expected {}`{{` after struct name", + if parsed_where { "" } else { "`where`, or " } + ), + ); return Err(err); } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 72e6f8a1bc..d9ec618403 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -140,7 +140,7 @@ impl<'a> Parser<'a> { } NonterminalKind::Ty => { - token::NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty())?) + token::NtTy(self.collect_tokens_no_attrs(|this| this.parse_no_question_mark_recover())?) } // this could be handled like a token, since it is one NonterminalKind::Ident diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 7f8fadb33b..48502112e3 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -4,8 +4,8 @@ use crate::maybe_whole; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; use rustc_ast::{ - self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocTyConstraint, - AssocTyConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocConstraint, + AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, Path, PathSegment, QSelf, }; use rustc_errors::{pluralize, Applicability, PResult}; @@ -139,22 +139,46 @@ impl<'a> Parser<'a> { style: PathStyle, ty_generics: Option<&Generics>, ) -> PResult<'a, Path> { - maybe_whole!(self, NtPath, |path| { + let reject_generics_if_mod_style = |parser: &Parser<'_>, path: &Path| { + // Ensure generic arguments don't end up in attribute paths, such as: + // + // macro_rules! m { + // ($p:path) => { #[$p] struct S; } + // } + // + // m!(inline); //~ ERROR: unexpected generic arguments in path + // if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some()) { - 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(); + parser + .struct_span_err( + path.segments + .iter() + .filter_map(|segment| segment.args.as_ref()) + .map(|arg| arg.span()) + .collect::>(), + "unexpected generic arguments in path", + ) + .emit(); } + }; + + maybe_whole!(self, NtPath, |path| { + reject_generics_if_mod_style(self, &path); path }); + if let token::Interpolated(nt) = &self.token.kind { + if let token::NtTy(ty) = &**nt { + if let ast::TyKind::Path(None, path) = &ty.kind { + let path = path.clone(); + self.bump(); + reject_generics_if_mod_style(self, &path); + return Ok(path); + } + } + } + let lo = self.token.span; let mut segments = Vec::new(); let mod_sep_ctxt = self.token.span.ctxt(); @@ -469,12 +493,9 @@ impl<'a> Parser<'a> { // Parse associated type constraint bound. let bounds = self.parse_generic_bounds(Some(self.prev_token.span))?; - AssocTyConstraintKind::Bound { bounds } + AssocConstraintKind::Bound { bounds } } else if self.eat(&token::Eq) { - // Parse associated type equality constraint - - let ty = self.parse_assoc_equality_term(ident, self.prev_token.span)?; - AssocTyConstraintKind::Equality { ty } + self.parse_assoc_equality_term(ident, self.prev_token.span)? } else { unreachable!(); }; @@ -482,11 +503,11 @@ impl<'a> Parser<'a> { let span = lo.to(self.prev_token.span); // Gate associated type bounds, e.g., `Iterator`. - if let AssocTyConstraintKind::Bound { .. } = kind { + if let AssocConstraintKind::Bound { .. } = kind { self.sess.gated_spans.gate(sym::associated_type_bounds, span); } let constraint = - AssocTyConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; + AssocConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; Ok(Some(AngleBracketedArg::Constraint(constraint))) } else { Ok(Some(AngleBracketedArg::Arg(arg))) @@ -499,22 +520,25 @@ impl<'a> Parser<'a> { /// Parse the term to the right of an associated item equality constraint. /// That is, parse `` in `Item = `. /// Right now, this only admits types in ``. - fn parse_assoc_equality_term(&mut self, ident: Ident, eq: Span) -> PResult<'a, P> { + fn parse_assoc_equality_term( + &mut self, + ident: Ident, + eq: Span, + ) -> PResult<'a, AssocConstraintKind> { let arg = self.parse_generic_arg(None)?; let span = ident.span.to(self.prev_token.span); - match arg { - Some(GenericArg::Type(ty)) => return Ok(ty), - Some(GenericArg::Const(expr)) => { - self.struct_span_err(span, "cannot constrain an associated constant to a value") - .span_label(ident.span, "this associated constant...") - .span_label(expr.value.span, "...cannot be constrained to this value") - .emit(); + let term = match arg { + Some(GenericArg::Type(ty)) => ty.into(), + Some(GenericArg::Const(c)) => { + self.sess.gated_spans.gate(sym::associated_const_equality, span); + c.into() } Some(GenericArg::Lifetime(lt)) => { self.struct_span_err(span, "associated lifetimes are not supported") .span_label(lt.ident.span, "the lifetime is given here") .help("if you meant to specify a trait object, write `dyn Trait + 'lifetime`") .emit(); + self.mk_ty(span, ast::TyKind::Err).into() } None => { let after_eq = eq.shrink_to_hi(); @@ -542,8 +566,8 @@ impl<'a> Parser<'a> { }; return Err(err); } - } - Ok(self.mk_ty(span, ast::TyKind::Err)) + }; + Ok(AssocConstraintKind::Equality { term }) } /// We do not permit arbitrary expressions as const arguments. They must be one of: diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 02a774ba12..89595c3f62 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -44,6 +44,11 @@ pub(super) enum RecoverQPath { No, } +pub(super) enum RecoverQuestionMark { + Yes, + No, +} + /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: @@ -100,6 +105,7 @@ impl<'a> Parser<'a> { RecoverQPath::Yes, RecoverReturnSign::Yes, None, + RecoverQuestionMark::Yes, ) } @@ -113,6 +119,7 @@ impl<'a> Parser<'a> { RecoverQPath::Yes, RecoverReturnSign::Yes, Some(ty_params), + RecoverQuestionMark::Yes, ) } @@ -126,6 +133,7 @@ impl<'a> Parser<'a> { RecoverQPath::Yes, RecoverReturnSign::Yes, None, + RecoverQuestionMark::Yes, ) } @@ -142,6 +150,31 @@ impl<'a> Parser<'a> { RecoverQPath::Yes, RecoverReturnSign::Yes, None, + RecoverQuestionMark::Yes, + ) + } + + /// Parses a type following an `as` cast. Similar to `parse_ty_no_plus`, but signaling origin + /// for better diagnostics involving `?`. + pub(super) fn parse_as_cast_ty(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::No, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::No, + ) + } + + pub(super) fn parse_no_question_mark_recover(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::No, ) } @@ -153,6 +186,7 @@ impl<'a> Parser<'a> { RecoverQPath::Yes, RecoverReturnSign::OnlyFatArrow, None, + RecoverQuestionMark::Yes, ) } @@ -171,6 +205,7 @@ impl<'a> Parser<'a> { recover_qpath, recover_return_sign, None, + RecoverQuestionMark::Yes, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -191,6 +226,7 @@ impl<'a> Parser<'a> { recover_qpath, recover_return_sign, None, + RecoverQuestionMark::Yes, )?; FnRetTy::Ty(ty) } else { @@ -205,6 +241,7 @@ impl<'a> Parser<'a> { recover_qpath: RecoverQPath, recover_return_sign: RecoverReturnSign, ty_generics: Option<&Generics>, + recover_question_mark: RecoverQuestionMark, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -280,6 +317,7 @@ impl<'a> Parser<'a> { // Try to recover from use of `+` with incorrect priority. self.maybe_report_ambiguous_plus(allow_plus, impl_dyn_multi, &ty); self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?; + let ty = self.maybe_recover_from_question_mark(ty, recover_question_mark); self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery) } diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 9d653de910..a6a2cbc277 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -95,7 +95,7 @@ pub enum Position { /// The argument is located at a specific index given in the format ArgumentIs(usize), /// The argument has a name. - ArgumentNamed(Symbol), + ArgumentNamed(Symbol, InnerSpan), } impl Position { @@ -147,7 +147,7 @@ pub enum Count { /// The count is specified explicitly. CountIs(usize), /// The count is specified by the argument with the given name. - CountIsName(Symbol), + CountIsName(Symbol, InnerSpan), /// The count is specified by the argument at the given index. CountIsParam(usize), /// The count is implied and cannot be explicitly specified. @@ -494,8 +494,11 @@ impl<'a> Parser<'a> { Some(ArgumentIs(i)) } else { match self.cur.peek() { - Some(&(_, c)) if rustc_lexer::is_id_start(c) => { - Some(ArgumentNamed(Symbol::intern(self.word()))) + Some(&(start, c)) if rustc_lexer::is_id_start(c) => { + let word = self.word(); + let end = start + word.len(); + let span = self.to_span_index(start).to(self.to_span_index(end)); + Some(ArgumentNamed(Symbol::intern(word), span)) } // This is an `ArgumentNext`. @@ -662,8 +665,9 @@ impl<'a> Parser<'a> { if word.is_empty() { self.cur = tmp; (CountImplied, None) - } else if self.consume('$') { - (CountIsName(Symbol::intern(word)), None) + } else if let Some(end) = self.consume_pos('$') { + let span = self.to_span_index(start + 1).to(self.to_span_index(end)); + (CountIsName(Symbol::intern(word), span), None) } else { self.cur = tmp; (CountImplied, None) diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index b7693a85ad..6c960fdc72 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -221,8 +221,8 @@ fn format_counts() { fill: None, align: AlignUnknown, flags: 0, - precision: CountIsName(Symbol::intern("b")), - width: CountIsName(Symbol::intern("a")), + precision: CountIsName(Symbol::intern("b"), InnerSpan::new(6, 7)), + width: CountIsName(Symbol::intern("a"), InnerSpan::new(4, 4)), precision_span: None, width_span: None, ty: "?", diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d7b0069949..479a08e43c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -4,19 +4,18 @@ //! conflicts between multiple such attributes attached to the same //! item. -use rustc_middle::hir::map::Map; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; - use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability}; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID}; use rustc_hir::{MethodKind, Target}; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; @@ -32,7 +31,7 @@ pub(crate) fn target_from_impl_item<'tcx>( match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, hir::ImplItemKind::Fn(..) => { - let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id()).expect_owner(); + 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(impl_) => impl_.of_trait.is_some(), @@ -63,7 +62,7 @@ impl CheckAttrVisitor<'_> { fn check_attributes( &self, hir_id: HirId, - span: &Span, + span: Span, target: Target, item: Option>, ) { @@ -77,9 +76,12 @@ impl CheckAttrVisitor<'_> { sym::inline => self.check_inline(hir_id, attr, span, target), sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target), sym::marker => self.check_marker(hir_id, attr, span, target), + sym::rustc_must_implement_one_of => { + self.check_rustc_must_implement_one_of(attr, span, target) + } sym::target_feature => self.check_target_feature(hir_id, attr, span, target), sym::track_caller => { - self.check_track_caller(hir_id, &attr.span, attrs, span, target) + self.check_track_caller(hir_id, attr.span, attrs, span, target) } sym::doc => self.check_doc_attrs( attr, @@ -104,6 +106,9 @@ impl CheckAttrVisitor<'_> { sym::rustc_legacy_const_generics => { self.check_rustc_legacy_const_generics(&attr, span, target, item) } + sym::rustc_lint_query_instability => { + self.check_rustc_lint_query_instability(&attr, span, target) + } sym::rustc_clean | sym::rustc_dirty | sym::rustc_if_this_changed @@ -114,6 +119,7 @@ impl CheckAttrVisitor<'_> { } sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target), sym::must_use => self.check_must_use(hir_id, &attr, span, target), + sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target), sym::rustc_const_unstable | sym::rustc_const_stable | sym::unstable @@ -126,6 +132,7 @@ impl CheckAttrVisitor<'_> { // lint-only checks match attr.name_or_empty() { sym::cold => self.check_cold(hir_id, attr, span, target), + sym::link => self.check_link(hir_id, attr, span, target), sym::link_name => self.check_link_name(hir_id, attr, span, target), sym::link_section => self.check_link_section(hir_id, attr, span, target), sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target), @@ -134,7 +141,6 @@ impl CheckAttrVisitor<'_> { } sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target), sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]), - sym::cfg_attr => self.check_cfg_attr(hir_id, attr), sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target), sym::macro_export => self.check_macro_export(hir_id, attr, target), sym::ignore | sym::should_panic | sym::proc_macro_derive => { @@ -250,7 +256,7 @@ impl CheckAttrVisitor<'_> { } /// 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 { + fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { match target { Target::Fn | Target::Closure @@ -293,7 +299,7 @@ impl CheckAttrVisitor<'_> { E0518, "attribute should be applied to function or closure", ) - .span_label(*span, "not a function or closure") + .span_label(span, "not a function or closure") .emit(); false } @@ -332,7 +338,7 @@ impl CheckAttrVisitor<'_> { } /// Checks if `#[naked]` is applied to a function definition. - fn check_naked(&self, hir_id: HirId, 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, @@ -351,7 +357,7 @@ impl CheckAttrVisitor<'_> { attr.span, "attribute should be applied to a function definition", ) - .span_label(*span, "not a function definition") + .span_label(span, "not a function definition") .emit(); false } @@ -359,7 +365,7 @@ impl CheckAttrVisitor<'_> { } /// Checks if `#[cmse_nonsecure_entry]` is applied to a function definition. - fn check_cmse_nonsecure_entry(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + fn check_cmse_nonsecure_entry(&self, attr: &Attribute, span: Span, target: Target) -> bool { match target { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, @@ -370,7 +376,7 @@ impl CheckAttrVisitor<'_> { attr.span, "attribute should be applied to a function definition", ) - .span_label(*span, "not a function definition") + .span_label(span, "not a function definition") .emit(); false } @@ -381,16 +387,16 @@ impl CheckAttrVisitor<'_> { fn check_track_caller( &self, hir_id: HirId, - attr_span: &Span, + attr_span: Span, attrs: &[Attribute], - span: &Span, + span: Span, target: Target, ) -> bool { match target { _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => { struct_span_err!( self.tcx.sess, - *attr_span, + attr_span, E0736, "cannot use `#[track_caller]` with `#[naked]`", ) @@ -411,11 +417,11 @@ impl CheckAttrVisitor<'_> { _ => { struct_span_err!( self.tcx.sess, - *attr_span, + attr_span, E0739, "attribute should be applied to function" ) - .span_label(*span, "not a function") + .span_label(span, "not a function") .emit(); false } @@ -427,7 +433,7 @@ impl CheckAttrVisitor<'_> { &self, hir_id: HirId, attr: &Attribute, - span: &Span, + span: Span, target: Target, ) -> bool { match target { @@ -447,7 +453,7 @@ impl CheckAttrVisitor<'_> { E0701, "attribute can only be applied to a struct or enum" ) - .span_label(*span, "not a struct or enum") + .span_label(span, "not a struct or enum") .emit(); false } @@ -455,7 +461,7 @@ impl CheckAttrVisitor<'_> { } /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid. - fn check_marker(&self, hir_id: HirId, 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 @@ -470,7 +476,27 @@ impl CheckAttrVisitor<'_> { self.tcx .sess .struct_span_err(attr.span, "attribute can only be applied to a trait") - .span_label(*span, "not a trait") + .span_label(span, "not a trait") + .emit(); + false + } + } + } + + /// Checks if the `#[rustc_must_implement_one_of]` attribute on a `target` is valid. Returns `true` if valid. + fn check_rustc_must_implement_one_of( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Trait => true, + _ => { + self.tcx + .sess + .struct_span_err(attr.span, "attribute can only be applied to a trait") + .span_label(span, "not a trait") .emit(); false } @@ -482,7 +508,7 @@ impl CheckAttrVisitor<'_> { &self, hir_id: HirId, attr: &Attribute, - span: &Span, + span: Span, target: Target, ) -> bool { match target { @@ -498,7 +524,7 @@ impl CheckAttrVisitor<'_> { being phased out; it will become a hard error in \ a future release!", ) - .span_label(*span, "not a function") + .span_label(span, "not a function") .emit(); }); true @@ -515,7 +541,7 @@ impl CheckAttrVisitor<'_> { self.tcx .sess .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(*span, "not a function") + .span_label(span, "not a function") .emit(); false } @@ -582,7 +608,7 @@ impl CheckAttrVisitor<'_> { Target::Impl => Some("implementation block"), Target::ForeignMod => Some("extern block"), Target::AssocTy => { - let parent_hir_id = self.tcx.hir().get_parent_item(hir_id).expect_owner(); + let parent_hir_id = self.tcx.hir().get_parent_item(hir_id); let containing_item = self.tcx.hir().expect_item(parent_hir_id); if Target::from_item(containing_item) == Target::Impl { Some("type alias in implementation block") @@ -591,7 +617,7 @@ impl CheckAttrVisitor<'_> { } } Target::AssocConst => { - let parent_hir_id = self.tcx.hir().get_parent_item(hir_id).expect_owner(); + let parent_hir_id = self.tcx.hir().get_parent_item(hir_id); let containing_item = self.tcx.hir().expect_item(parent_hir_id); // We can't link to trait impl's consts. let err = "associated constant in trait implementation block"; @@ -832,7 +858,7 @@ impl CheckAttrVisitor<'_> { let mut err = lint.build( "this attribute can only be applied at the crate level", ); - if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_HIR_ID { + if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID { if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) { @@ -1066,14 +1092,26 @@ impl CheckAttrVisitor<'_> { is_valid } + /// Warns against some misuses of `#[pass_by_value]` + fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> bool { + match target { + Target::Struct | Target::Enum | Target::TyAlias => true, + _ => { + self.tcx + .sess + .struct_span_err( + attr.span, + "`pass_by_value` attribute should be applied to a struct, enum or type alias.", + ) + .span_label(span, "is not a struct, enum or type alias") + .emit(); + false + } + } + } + /// Warns against some misuses of `#[must_use]` - fn check_must_use( - &self, - hir_id: HirId, - attr: &Attribute, - span: &Span, - _target: Target, - ) -> bool { + fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, _target: Target) -> bool { let node = self.tcx.hir().get(hir_id); if let Some(fn_node) = node.fn_kind() { if let rustc_hir::IsAsync::Async = fn_node.asyncness() { @@ -1084,7 +1122,7 @@ impl CheckAttrVisitor<'_> { function, not the value within", ) .span_label( - *span, + span, "this attribute does nothing, the `Future`s \ returned by async functions are already `must_use`", ) @@ -1098,14 +1136,14 @@ impl CheckAttrVisitor<'_> { } /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid. - fn check_must_not_suspend(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) -> bool { match target { Target::Struct | Target::Enum | Target::Union | Target::Trait => true, _ => { self.tcx .sess .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait") - .span_label(*span, "is not a struct, enum, or trait") + .span_label(span, "is not a struct, enum, or trait") .emit(); false } @@ -1113,7 +1151,7 @@ impl CheckAttrVisitor<'_> { } /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid. - fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { + 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 @@ -1133,15 +1171,35 @@ impl CheckAttrVisitor<'_> { being phased out; it will become a hard error in \ a future release!", ) - .span_label(*span, "not a function") + .span_label(span, "not a function") .emit(); }); } } } + /// Checks if `#[link]` is applied to an item other than a foreign module. + fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + match target { + Target::ForeignMod => {} + _ => { + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + let mut diag = lint.build("attribute should be applied to an `extern` block"); + diag.warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ); + + diag.span_label(span, "not an `extern` block"); + diag.emit(); + }); + } + } + } + /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. - fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { + 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 @@ -1175,7 +1233,7 @@ impl CheckAttrVisitor<'_> { } } - diag.span_label(*span, "not a foreign function or static"); + diag.span_label(span, "not a foreign function or static"); diag.emit(); }); } @@ -1183,7 +1241,7 @@ impl CheckAttrVisitor<'_> { } /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid. - fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool { + 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 @@ -1201,7 +1259,7 @@ impl CheckAttrVisitor<'_> { attr.span, "attribute should be applied to an `extern crate` item", ) - .span_label(*span, "not an `extern crate` item") + .span_label(span, "not an `extern crate` item") .emit(); false } @@ -1217,7 +1275,7 @@ impl CheckAttrVisitor<'_> { &self, hir_id: HirId, attr: &Attribute, - span: &Span, + span: Span, target: Target, ) -> bool { match target { @@ -1238,7 +1296,7 @@ impl CheckAttrVisitor<'_> { attr.span, "attribute should be applied to a free function, impl method or static", ) - .span_label(*span, "not a free function, impl method or static") + .span_label(span, "not a free function, impl method or static") .emit(); false } @@ -1248,14 +1306,14 @@ impl CheckAttrVisitor<'_> { fn check_rustc_layout_scalar_valid_range( &self, attr: &Attribute, - span: &Span, + span: Span, target: Target, ) -> bool { if target != Target::Struct { self.tcx .sess .struct_span_err(attr.span, "attribute should be applied to a struct") - .span_label(*span, "not a struct") + .span_label(span, "not a struct") .emit(); return false; } @@ -1280,7 +1338,7 @@ impl CheckAttrVisitor<'_> { fn check_rustc_legacy_const_generics( &self, attr: &Attribute, - span: &Span, + span: Span, target: Target, item: Option>, ) -> bool { @@ -1289,7 +1347,7 @@ impl CheckAttrVisitor<'_> { self.tcx .sess .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(*span, "not a function") + .span_label(span, "not a function") .emit(); return false; } @@ -1375,6 +1433,25 @@ impl CheckAttrVisitor<'_> { } } + fn check_rustc_lint_query_instability( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + let is_function = matches!(target, Target::Fn | Target::Method(..)); + if !is_function { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a function") + .span_label(span, "not a function") + .emit(); + false + } else { + true + } + } + /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { @@ -1390,7 +1467,7 @@ impl CheckAttrVisitor<'_> { } /// Checks if `#[link_section]` is applied to a function or static. - fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { + 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 @@ -1410,7 +1487,7 @@ impl CheckAttrVisitor<'_> { being phased out; it will become a hard error in \ a future release!", ) - .span_label(*span, "not a function or static") + .span_label(span, "not a function or static") .emit(); }); } @@ -1418,7 +1495,7 @@ impl CheckAttrVisitor<'_> { } /// Checks if `#[no_mangle]` is applied to a function or static. - fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { + fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { Target::Static | Target::Fn => {} Target::Method(..) if self.is_impl_item(hir_id) => {} @@ -1448,7 +1525,7 @@ impl CheckAttrVisitor<'_> { being phased out; it will become a hard error in \ a future release!", ) - .span_label(*span, format!("foreign {}", foreign_item_kind)) + .span_label(span, format!("foreign {}", foreign_item_kind)) .note("symbol names in extern blocks are not mangled") .span_suggestion( attr.span, @@ -1471,7 +1548,7 @@ impl CheckAttrVisitor<'_> { being phased out; it will become a hard error in \ a future release!", ) - .span_label(*span, "not a free function, impl method or static") + .span_label(span, "not a free function, impl method or static") .emit(); }); } @@ -1482,7 +1559,7 @@ impl CheckAttrVisitor<'_> { fn check_repr( &self, attrs: &[Attribute], - span: &Span, + span: Span, target: Target, item: Option>, hir_id: HirId, @@ -1616,7 +1693,7 @@ impl CheckAttrVisitor<'_> { "{}", &format!("attribute should be applied to {} {}", article, allowed_targets) ) - .span_label(*span, &format!("not {} {}", article, allowed_targets)) + .span_label(span, &format!("not {} {}", article, allowed_targets)) .emit(); } @@ -1664,12 +1741,46 @@ impl CheckAttrVisitor<'_> { } fn check_used(&self, attrs: &[Attribute], target: Target) { + let mut used_linker_span = None; + let mut used_compiler_span = None; for attr in attrs { if attr.has_name(sym::used) && target != Target::Static { self.tcx .sess .span_err(attr.span, "attribute must be applied to a `static` variable"); } + let inner = attr.meta_item_list(); + match inner.as_deref() { + Some([item]) if item.has_name(sym::linker) => { + if used_linker_span.is_none() { + used_linker_span = Some(attr.span); + } + } + Some([item]) if item.has_name(sym::compiler) => { + if used_compiler_span.is_none() { + used_compiler_span = Some(attr.span); + } + } + Some(_) => { + // This error case is handled in rustc_typeck::collect. + } + None => { + // Default case (compiler) when arg isn't defined. + if used_compiler_span.is_none() { + used_compiler_span = Some(attr.span); + } + } + } + } + if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) { + let spans = vec![linker_span, compiler_span]; + self.tcx + .sess + .struct_span_err( + spans, + "`used(compiler)` and `used(linker)` can't be used together", + ) + .emit(); } } @@ -1679,7 +1790,7 @@ impl CheckAttrVisitor<'_> { &self, hir_id: HirId, attr: &Attribute, - span: &Span, + span: Span, target: Target, attrs: &[Attribute], ) -> bool { @@ -1712,7 +1823,7 @@ impl CheckAttrVisitor<'_> { self.tcx .sess .struct_span_err(attr.span, "attribute should be applied to a macro") - .span_label(*span, "not a macro") + .span_label(span, "not a macro") .emit(); false } @@ -1725,7 +1836,7 @@ impl CheckAttrVisitor<'_> { &self, hir_id: HirId, attr: &Attribute, - span: &Span, + span: Span, target: Target, ) -> bool { match target { @@ -1746,7 +1857,7 @@ impl CheckAttrVisitor<'_> { self.tcx .sess .struct_span_err(attr.span, "attribute should be applied to `const fn`") - .span_label(*span, "not a `const fn`") + .span_label(span, "not a `const fn`") .emit(); false } @@ -1757,7 +1868,7 @@ impl CheckAttrVisitor<'_> { fn check_default_method_body_is_const( &self, attr: &Attribute, - span: &Span, + span: Span, target: Target, ) -> bool { match target { @@ -1769,14 +1880,14 @@ impl CheckAttrVisitor<'_> { attr.span, "attribute should be applied to a trait method with body", ) - .span_label(*span, "not a trait method or missing a body") + .span_label(span, "not a trait method or missing a body") .emit(); false } } } - fn check_stability_promotable(&self, attr: &Attribute, _span: &Span, target: Target) -> bool { + fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool { match target { Target::Expression => { self.tcx @@ -1789,7 +1900,7 @@ impl CheckAttrVisitor<'_> { } } - fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: &Span, target: Target) { + fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) { match target { Target::Closure | Target::Expression | Target::Statement | Target::Arm => { self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { @@ -1823,16 +1934,6 @@ impl CheckAttrVisitor<'_> { } } - fn check_cfg_attr(&self, hir_id: HirId, attr: &Attribute) { - if let Some((_, attrs)) = rustc_parse::parse_cfg_attr(&attr, &self.tcx.sess.parse_sess) { - if attrs.is_empty() { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[cfg_attr]` does not expand to any attributes").emit(); - }); - } - } - } - fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) { if target != Target::Fn { self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { @@ -1843,10 +1944,10 @@ impl CheckAttrVisitor<'_> { } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx Item<'tcx>) { @@ -1861,29 +1962,29 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { } let target = Target::from_item(item); - self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item))); + self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item))); intravisit::walk_item(self, item) } fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) { let target = Target::from_generic_param(generic_param); - self.check_attributes(generic_param.hir_id, &generic_param.span, target, None); + self.check_attributes(generic_param.hir_id, generic_param.span, target, None); intravisit::walk_generic_param(self, generic_param) } fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) { let target = Target::from_trait_item(trait_item); - self.check_attributes(trait_item.hir_id(), &trait_item.span, target, None); + self.check_attributes(trait_item.hir_id(), trait_item.span, target, None); intravisit::walk_trait_item(self, trait_item) } fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) { - self.check_attributes(struct_field.hir_id, &struct_field.span, Target::Field, None); + self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None); intravisit::walk_field_def(self, struct_field); } fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { - self.check_attributes(arm.hir_id, &arm.span, Target::Arm, None); + self.check_attributes(arm.hir_id, arm.span, Target::Arm, None); intravisit::walk_arm(self, arm); } @@ -1891,7 +1992,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { let target = Target::from_foreign_item(f_item); self.check_attributes( f_item.hir_id(), - &f_item.span, + f_item.span, target, Some(ItemLike::ForeignItem(f_item)), ); @@ -1900,14 +2001,14 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { let target = target_from_impl_item(self.tcx, impl_item); - self.check_attributes(impl_item.hir_id(), &impl_item.span, target, None); + self.check_attributes(impl_item.hir_id(), impl_item.span, target, None); intravisit::walk_impl_item(self, impl_item) } fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { // When checking statements ignore expressions, they will be checked later. if let hir::StmtKind::Local(ref l) = stmt.kind { - self.check_attributes(l.hir_id, &stmt.span, Target::Statement, None); + self.check_attributes(l.hir_id, stmt.span, Target::Statement, None); } intravisit::walk_stmt(self, stmt) } @@ -1918,7 +2019,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { _ => Target::Expression, }; - self.check_attributes(expr.hir_id, &expr.span, target, None); + self.check_attributes(expr.hir_id, expr.span, target, None); intravisit::walk_expr(self, expr) } @@ -1928,12 +2029,12 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { generics: &'tcx hir::Generics<'tcx>, item_id: HirId, ) { - self.check_attributes(variant.id, &variant.span, Target::Variant, None); + self.check_attributes(variant.id, variant.span, Target::Variant, None); intravisit::walk_variant(self, variant, generics, item_id) } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - self.check_attributes(param.hir_id, ¶m.span, Target::Param, None); + self.check_attributes(param.hir_id, param.span, Target::Param, None); intravisit::walk_param(self, param); } @@ -2025,7 +2126,7 @@ fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let check_attr_visitor = &mut CheckAttrVisitor { tcx }; tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor()); if module_def_id.is_top_level_module() { - check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None); + check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None); check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs()); } } diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index a5a6574070..2b11f6b0c1 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -11,8 +11,8 @@ use rustc_attr as attr; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_middle::hir::map::Map; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_middle::hir::nested_filter; use rustc_middle::ty; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; @@ -93,26 +93,29 @@ impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor< for trait_item in self.tcx.associated_items(trait_def_id).in_definition_order() { if let ty::AssocItem { - kind: ty::AssocKind::Fn, ident, defaultness, .. - } = trait_item + kind: ty::AssocKind::Fn, + defaultness, + def_id: trait_item_id, + .. + } = *trait_item { // we can ignore functions that do not have default bodies: // if those are unimplemented it will be catched by typeck. if !defaultness.has_value() || self .tcx - .has_attr(trait_item.def_id, sym::default_method_body_is_const) + .has_attr(trait_item_id, sym::default_method_body_is_const) { continue; } let is_implemented = ancestors - .leaf_def(self.tcx, trait_item.ident, trait_item.kind) + .leaf_def(self.tcx, trait_item_id) .map(|node_item| !node_item.defining_node.is_from_trait()) .unwrap_or(false); if !is_implemented { - to_implement.push(ident.to_string()); + to_implement.push(self.tcx.item_name(trait_item_id).to_string()); } } } @@ -259,10 +262,10 @@ impl<'tcx> CheckConstVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 3b15332c67..e52fbc8ab9 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -3,18 +3,21 @@ // from live codes are live, and everything else is dead. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{Node, PatKind, TyKind}; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy; +use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::Span; use std::mem; // Any local node that may call something in its body block should be @@ -23,7 +26,7 @@ use std::mem; // may need to be marked as live. fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { matches!( - tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)), + tcx.hir().find_by_def_id(def_id), Some( Node::Item(..) | Node::ImplItem(..) @@ -47,6 +50,10 @@ struct MarkSymbolVisitor<'tcx> { ignore_variant_stack: Vec, // maps from tuple struct constructors to tuple struct items struct_constructors: FxHashMap, + // maps from ADTs to ignored derived traits (e.g. Debug and Clone) + // and the span of their respective impl (i.e., part of the derive + // macro) + ignored_derived_traits: FxHashMap>, } impl<'tcx> MarkSymbolVisitor<'tcx> { @@ -97,7 +104,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { self.check_def_id(variant_id); } } - Res::SelfTy(t, i) => { + Res::SelfTy { trait_: t, alias_to: i } => { if let Some(t) = t { self.check_def_id(t); } @@ -232,7 +239,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { // tuple struct constructor function let id = self.struct_constructors.get(&id).copied().unwrap_or(id); - if let Some(node) = self.tcx.hir().find(self.tcx.hir().local_def_id_to_hir_id(id)) { + if let Some(node) = self.tcx.hir().find_by_def_id(id) { self.live_symbols.insert(id); self.visit_node(node); } @@ -242,7 +249,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// Automatically generated items marked with `rustc_trivial_field_reads` /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). - fn should_ignore_item(&self, def_id: DefId) -> bool { + fn should_ignore_item(&mut self, def_id: DefId) -> bool { if let Some(impl_of) = self.tcx.impl_of_method(def_id) { if !self.tcx.has_attr(impl_of, sym::automatically_derived) { return false; @@ -250,6 +257,15 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) { if self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) { + let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap(); + if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() { + if let Some(adt_def_id) = adt_def.did.as_local() { + self.ignored_derived_traits + .entry(adt_def_id) + .or_default() + .push((trait_of, impl_of)); + } + } return true; } } @@ -323,12 +339,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_nested_body(&mut self, body: hir::BodyId) { let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.tcx.typeck_body(body)); @@ -456,7 +466,10 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { // #[used], #[no_mangle], #[export_name], etc also keeps the item alive // forcefully, e.g., for placing it in a specific section. - if cg_attrs.contains_extern_indicator() || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) { + if cg_attrs.contains_extern_indicator() + || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) + || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) + { return true; } @@ -553,8 +566,8 @@ impl<'v, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'tcx> { fn create_and_seed_worklist<'tcx>( tcx: TyCtxt<'tcx>, - access_levels: &privacy::AccessLevels, ) -> (Vec, FxHashMap) { + let access_levels = &tcx.privacy_access_levels(()); let worklist = access_levels .map .iter() @@ -574,11 +587,11 @@ fn create_and_seed_worklist<'tcx>( (life_seeder.worklist, life_seeder.struct_constructors) } -fn find_live<'tcx>( +fn live_symbols_and_ignored_derived_traits<'tcx>( tcx: TyCtxt<'tcx>, - access_levels: &privacy::AccessLevels, -) -> FxHashSet { - let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels); + (): (), +) -> (FxHashSet, FxHashMap>) { + let (worklist, struct_constructors) = create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -590,14 +603,16 @@ fn find_live<'tcx>( pub_visibility: false, ignore_variant_stack: vec![], struct_constructors, + ignored_derived_traits: FxHashMap::default(), }; symbol_visitor.mark_live_symbols(); - symbol_visitor.live_symbols + (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits) } struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, - live_symbols: FxHashSet, + live_symbols: &'tcx FxHashSet, + ignored_derived_traits: &'tcx FxHashMap>, } impl<'tcx> DeadVisitor<'tcx> { @@ -666,21 +681,52 @@ impl<'tcx> DeadVisitor<'tcx> { self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| { let def_id = self.tcx.hir().local_def_id(id); let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); - lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit() + let mut err = lint.build(&format!("{} is never {}: `{}`", descr, participle, name)); + let hir = self.tcx.hir(); + if let Some(encl_scope) = hir.get_enclosing_scope(id) { + if let Some(encl_def_id) = hir.opt_local_def_id(encl_scope) { + if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) { + let traits_str = ign_traits + .iter() + .map(|(trait_id, _)| format!("`{}`", self.tcx.item_name(*trait_id))) + .collect::>() + .join(" and "); + let plural_s = pluralize!(ign_traits.len()); + let article = if ign_traits.len() > 1 { "" } else { "a " }; + let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" }; + let msg = format!( + "`{}` has {}derived impl{} for the trait{} {}, but {} \ + intentionally ignored during dead code analysis", + self.tcx.item_name(encl_def_id.to_def_id()), + article, + plural_s, + plural_s, + traits_str, + is_are + ); + let multispan = ign_traits + .iter() + .map(|(_, impl_id)| self.tcx.def_span(*impl_id)) + .collect::>(); + err.span_note(multispan, &msg); + } + } + } + err.emit(); }); } } } impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; /// Walk nested items in place so that we don't report dead-code /// on inner functions when the outer function is already getting /// an error. We could do this also by checking the parents, but /// this is how the code is setup and it seems harmless enough. - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { @@ -719,6 +765,9 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> { } } + // This visitor should only visit a single module at a time. + fn visit_mod(&mut self, _: &'tcx hir::Mod<'tcx>, _: Span, _: hir::HirId) {} + fn visit_variant( &mut self, variant: &'tcx hir::Variant<'tcx>, @@ -794,9 +843,16 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> { } } -pub fn check_crate(tcx: TyCtxt<'_>) { - let access_levels = &tcx.privacy_access_levels(()); - let live_symbols = find_live(tcx, access_levels); - let mut visitor = DeadVisitor { tcx, live_symbols }; - tcx.hir().walk_toplevel_module(&mut visitor); +fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalDefId) { + let (live_symbols, ignored_derived_traits) = tcx.live_symbols_and_ignored_derived_traits(()); + let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits }; + let (module, _, module_id) = tcx.hir().get_module(module); + // Do not use an ItemLikeVisitor since we may want to skip visiting some items + // when a surrounding one is warned against or `_`. + intravisit::walk_mod(&mut visitor, module, module_id); +} + +pub(crate) fn provide(providers: &mut Providers) { + *providers = + Providers { live_symbols_and_ignored_derived_traits, check_mod_deathness, ..*providers }; } diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 63f9b3ed6b..fdabe41daf 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -1,8 +1,8 @@ use rustc_ast::entry::EntryPointType; use rustc_errors::struct_span_err; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::{ForeignItem, HirId, ImplItem, Item, ItemKind, Node, TraitItem, CRATE_HIR_ID}; +use rustc_hir::{ForeignItem, ImplItem, Item, ItemKind, Node, TraitItem, CRATE_HIR_ID}; use rustc_middle::hir::map::Map; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; @@ -18,14 +18,14 @@ struct EntryContext<'a, 'tcx> { map: Map<'tcx>, /// The function that has attribute named `main`. - attr_main_fn: Option<(HirId, Span)>, + attr_main_fn: Option<(LocalDefId, Span)>, /// The function that has the attribute 'start' on it. - start_fn: Option<(HirId, Span)>, + start_fn: Option<(LocalDefId, Span)>, /// The functions that one might think are `main` but aren't, e.g. /// main functions not defined at the top level. For diagnostics. - non_main_fns: Vec<(HirId, Span)>, + non_main_fns: Vec, } impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> { @@ -112,11 +112,11 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { } EntryPointType::MainNamed => (), EntryPointType::OtherMain => { - ctxt.non_main_fns.push((item.hir_id(), item.span)); + ctxt.non_main_fns.push(item.span); } EntryPointType::MainAttr => { if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((item.hir_id(), item.span)); + ctxt.attr_main_fn = Some((item.def_id, item.span)); } else { struct_span_err!( ctxt.session, @@ -131,7 +131,7 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { } EntryPointType::Start => { if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((item.hir_id(), item.span)); + ctxt.start_fn = Some((item.def_id, item.span)); } else { struct_span_err!(ctxt.session, item.span, E0138, "multiple `start` functions") .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here") @@ -143,20 +143,19 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { } fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) -> Option<(DefId, EntryFnType)> { - if let Some((hir_id, _)) = visitor.start_fn { - Some((tcx.hir().local_def_id(hir_id).to_def_id(), EntryFnType::Start)) - } else if let Some((hir_id, _)) = visitor.attr_main_fn { - Some((tcx.hir().local_def_id(hir_id).to_def_id(), EntryFnType::Main)) + if let Some((def_id, _)) = visitor.start_fn { + Some((def_id.to_def_id(), EntryFnType::Start)) + } else if let Some((def_id, _)) = visitor.attr_main_fn { + Some((def_id.to_def_id(), EntryFnType::Main)) } else { if let Some(main_def) = tcx.resolutions(()).main_def { if let Some(def_id) = main_def.opt_fn_def_id() { // non-local main imports are handled below - if def_id.is_local() { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - if matches!(tcx.hir().find(hir_id), Some(Node::ForeignItem(_))) { + if let Some(def_id) = def_id.as_local() { + if matches!(tcx.hir().find_by_def_id(def_id), Some(Node::ForeignItem(_))) { tcx.sess .struct_span_err( - tcx.hir().span(hir_id), + tcx.def_span(def_id), "the `main` function cannot be declared in an `extern` block", ) .emit(); @@ -201,7 +200,7 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) { ); let filename = &tcx.sess.local_crate_source_file; let note = if !visitor.non_main_fns.is_empty() { - for &(_, span) in &visitor.non_main_fns { + for &span in &visitor.non_main_fns { err.span_note(span, "here is a function named `main`"); } err.note("you have one or more functions named `main` not defined at the crate level"); diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index 0e60ca9f90..56755d6868 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -6,6 +6,7 @@ use rustc_hir::intravisit; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{HirId, ItemLocalId}; use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; pub fn check_crate(tcx: TyCtxt<'_>) { @@ -57,22 +58,22 @@ impl<'a, 'hir> OuterVisitor<'a, 'hir> { impl<'a, 'hir> ItemLikeVisitor<'hir> for OuterVisitor<'a, 'hir> { fn visit_item(&mut self, i: &'hir hir::Item<'hir>) { let mut inner_visitor = self.new_inner_visitor(self.hir_map); - inner_visitor.check(i.hir_id(), |this| intravisit::walk_item(this, i)); + inner_visitor.check(i.def_id, |this| intravisit::walk_item(this, i)); } fn visit_trait_item(&mut self, i: &'hir hir::TraitItem<'hir>) { let mut inner_visitor = self.new_inner_visitor(self.hir_map); - inner_visitor.check(i.hir_id(), |this| intravisit::walk_trait_item(this, i)); + inner_visitor.check(i.def_id, |this| intravisit::walk_trait_item(this, i)); } fn visit_impl_item(&mut self, i: &'hir hir::ImplItem<'hir>) { let mut inner_visitor = self.new_inner_visitor(self.hir_map); - inner_visitor.check(i.hir_id(), |this| intravisit::walk_impl_item(this, i)); + inner_visitor.check(i.def_id, |this| intravisit::walk_impl_item(this, i)); } fn visit_foreign_item(&mut self, i: &'hir hir::ForeignItem<'hir>) { let mut inner_visitor = self.new_inner_visitor(self.hir_map); - inner_visitor.check(i.hir_id(), |this| intravisit::walk_foreign_item(this, i)); + inner_visitor.check(i.def_id, |this| intravisit::walk_foreign_item(this, i)); } } @@ -83,9 +84,8 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { self.errors.lock().push(f()); } - fn check)>(&mut self, hir_id: HirId, walk: F) { + fn check)>(&mut self, owner: LocalDefId, walk: F) { assert!(self.owner.is_none()); - let owner = self.hir_map.local_def_id(hir_id); self.owner = Some(owner); walk(self); @@ -140,10 +140,10 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { } impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { - type Map = Map<'hir>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::OnlyBodies(self.hir_map) + fn nested_visit_map(&mut self) -> Self::Map { + self.hir_map } fn visit_id(&mut self, hir_id: HirId) { diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index d665c12f76..6cf1aa480d 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -95,12 +95,6 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_param(self, param) } - type Map = Map<'v>; - - fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { - panic!("visit_nested_xxx must be manually implemented in this visitor") - } - fn visit_nested_item(&mut self, id: hir::ItemId) { let nested_item = self.krate.unwrap().item(id); self.visit_item(nested_item) @@ -338,9 +332,9 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_path_segment(self, path_span, path_segment) } - fn visit_assoc_ty_constraint(&mut self, constraint: &'v ast::AssocTyConstraint) { - self.record("AssocTyConstraint", Id::None, constraint); - ast_visit::walk_assoc_ty_constraint(self, constraint) + fn visit_assoc_constraint(&mut self, constraint: &'v ast::AssocConstraint) { + self.record("AssocConstraint", Id::None, constraint); + ast_visit::walk_assoc_constraint(self, constraint) } fn visit_attribute(&mut self, attr: &'v ast::Attribute) { diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 064c469662..1031ba01c1 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -3,7 +3,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_index::vec::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; use rustc_middle::ty::query::Providers; @@ -294,9 +294,8 @@ impl<'tcx> ExprVisitor<'tcx> { // (!). In that case we still need the earlier check to verify that the // register class is usable at all. if let Some(feature) = feature { - let feat_sym = Symbol::intern(feature); - if !self.tcx.sess.target_features.contains(&feat_sym) - && !target_features.contains(&feat_sym) + if !self.tcx.sess.target_features.contains(&feature) + && !target_features.contains(&feature) { let msg = &format!("`{}` target feature is not enabled", feature); let mut err = self.tcx.sess.struct_span_err(expr.span, msg); @@ -377,9 +376,8 @@ impl<'tcx> ExprVisitor<'tcx> { { match feature { Some(feature) => { - let feat_sym = Symbol::intern(feature); - if self.tcx.sess.target_features.contains(&feat_sym) - || attrs.target_features.contains(&feat_sym) + if self.tcx.sess.target_features.contains(&feature) + || attrs.target_features.contains(&feature) { missing_required_features.clear(); break; @@ -413,7 +411,11 @@ impl<'tcx> ExprVisitor<'tcx> { let msg = format!( "register class `{}` requires at least one of the following target features: {}", reg_class.name(), - features.join(", ") + features + .iter() + .map(|f| f.as_str()) + .intersperse(", ") + .collect::(), ); self.tcx.sess.struct_span_err(*op_sp, &msg).emit(); // register isn't enabled, don't do more checks @@ -488,12 +490,6 @@ impl<'tcx> ExprVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for ItemVisitor<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_nested_body(&mut self, body_id: hir::BodyId) { let owner_def_id = self.tcx.hir().body_owner_def_id(body_id); let body = self.tcx.hir().body(body_id); @@ -505,12 +501,6 @@ impl<'tcx> Visitor<'tcx> for ItemVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for ExprVisitor<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { hir::ExprKind::Path(ref qpath) => { diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index a808d6c834..0c934ecc91 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -10,7 +10,6 @@ use crate::check_attr::target_from_impl_item; use crate::weak_lang_items; -use rustc_ast::Attribute; use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -57,8 +56,7 @@ impl<'tcx> LanguageItemCollector<'tcx> { fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) { let attrs = self.tcx.hir().attrs(hir_id); - let check_name = |attr: &Attribute, sym| attr.has_name(sym); - if let Some((value, span)) = extract(check_name, &attrs) { + if let Some((value, span)) = extract(&attrs) { match ITEM_REFS.get(&value).cloned() { // Known lang item with attribute on correct target. Some((item_index, expected_target)) if actual_target == expected_target => { diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 8a411f01d6..3130513c40 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -6,11 +6,14 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(crate_visibility_modifier)] +#![feature(iter_intersperse)] +#![feature(let_else)] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(nll)] #![feature(try_blocks)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_middle; @@ -42,6 +45,7 @@ mod weak_lang_items; pub fn provide(providers: &mut Providers) { check_attr::provide(providers); check_const::provide(providers); + dead::provide(providers); diagnostic_items::provide(providers); entry::provide(providers); lang_items::provide(providers); diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 40d12c4a22..00445690f8 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -6,8 +6,8 @@ use rustc_ast::{Attribute, MetaItemKind}; use rustc_errors::struct_span_err; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_middle::hir::map::Map; +use rustc_hir::intravisit::Visitor; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::LibFeatures; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; @@ -111,10 +111,10 @@ impl<'tcx> LibFeatureCollector<'tcx> { } impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_attribute(&mut self, _: rustc_hir::HirId, attr: &'tcx Attribute) { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 9ee305b712..69cd1b4fed 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -90,10 +90,10 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::*; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet}; use rustc_index::vec::IndexVec; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; @@ -103,7 +103,6 @@ use rustc_span::Span; use std::collections::VecDeque; use std::io; use std::io::prelude::*; -use std::iter; use std::rc::Rc; mod rwu_table; @@ -317,10 +316,10 @@ impl<'tcx> IrMaps<'tcx> { } impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { @@ -470,7 +469,6 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) - | hir::ExprKind::LlvmInlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err @@ -726,7 +724,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { ); self.acc(self.exit_ln, var, ACC_READ | ACC_USE); } - ty::UpvarCapture::ByValue(_) => {} + ty::UpvarCapture::ByValue => {} } } } @@ -1091,26 +1089,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { succ } - hir::ExprKind::LlvmInlineAsm(ref asm) => { - let ia = &asm.inner; - let outputs = asm.outputs_exprs; - let inputs = asm.inputs_exprs; - let succ = iter::zip(&ia.outputs, outputs).rev().fold(succ, |succ, (o, output)| { - // see comment on places - // in propagate_through_place_components() - if o.is_indirect { - self.propagate_through_expr(output, succ) - } else { - let acc = if o.is_rw { ACC_WRITE | ACC_READ } else { ACC_WRITE }; - let succ = self.write_place(output, succ, acc); - self.propagate_through_place_components(output, succ) - } - }); - - // Inputs are executed first. Propagate last because of rev order - self.propagate_through_exprs(inputs, succ) - } - hir::ExprKind::Lit(..) | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Err @@ -1327,12 +1305,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Checking for error conditions impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| { if local.init.is_some() { @@ -1387,20 +1359,6 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { } } - hir::ExprKind::LlvmInlineAsm(ref asm) => { - for input in asm.inputs_exprs { - this.visit_expr(input); - } - - // Output operands must be places - for (o, output) in iter::zip(&asm.inner.outputs, asm.outputs_exprs) { - if !o.is_indirect { - this.check_place(output); - } - this.visit_expr(output); - } - } - hir::ExprKind::Let(let_expr) => { this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {}); } @@ -1481,7 +1439,7 @@ impl<'tcx> Liveness<'_, 'tcx> { for (&var_hir_id, min_capture_list) in closure_min_captures { for captured_place in min_capture_list { match captured_place.info.capture_kind { - ty::UpvarCapture::ByValue(_) => {} + ty::UpvarCapture::ByValue => {} ty::UpvarCapture::ByRef(..) => continue, }; let span = captured_place.get_capture_kind_span(self.ir.tcx); diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 4bfac1b729..02b09daf0a 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -3,9 +3,10 @@ use Context::*; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Destination, Movability, Node}; use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -41,10 +42,10 @@ pub(crate) fn provide(providers: &mut Providers) { } impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { - type Map = Map<'hir>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.hir_map) + fn nested_visit_map(&mut self) -> Self::Map { + self.hir_map } fn visit_anon_const(&mut self, c: &'hir hir::AnonConst) { diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 07cb165d79..00a93ccc9a 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -1,14 +1,14 @@ //! Checks validity of naked functions. use rustc_ast::{Attribute, InlineAsmOptions}; +use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{ErasedMap, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; -use rustc_session::lint::builtin::UNSUPPORTED_NAKED_FUNCTIONS; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi; @@ -29,12 +29,6 @@ struct CheckNakedFunctions<'tcx> { } impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> { - type Map = ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_fn( &mut self, fk: FnKind<'_>, @@ -70,18 +64,16 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> { check_abi(self.tcx, hir_id, fn_header.abi, ident_span); check_no_patterns(self.tcx, body.params); check_no_parameters_use(self.tcx, body); - check_asm(self.tcx, hir_id, body, span); - check_inline(self.tcx, hir_id, attrs); + check_asm(self.tcx, body, span); + check_inline(self.tcx, attrs); } } } /// Check that the function isn't inlined. -fn check_inline(tcx: TyCtxt<'_>, hir_id: HirId, attrs: &[Attribute]) { +fn check_inline(tcx: TyCtxt<'_>, attrs: &[Attribute]) { for attr in attrs.iter().filter(|attr| attr.has_name(sym::inline)) { - tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, attr.span, |lint| { - lint.build("naked functions cannot be inlined").emit(); - }); + tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit(); } } @@ -129,12 +121,6 @@ struct CheckParameters<'tcx> { } impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { - type Map = ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Path(hir::QPath::Resolved( _, @@ -158,31 +144,31 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { } /// Checks that function body contains a single inline assembly block. -fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId, body: &'tcx hir::Body<'tcx>, fn_span: Span) { +fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, 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[..] { // Ok. } else { - tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_span, |lint| { - let mut diag = lint.build("naked functions must contain a single asm block"); - let mut has_asm = false; - for &(kind, span) in &this.items { - match kind { - ItemKind::Asm if has_asm => { - diag.span_label( - span, - "multiple asm blocks are unsupported in naked functions", - ); - } - ItemKind::Asm => has_asm = true, - ItemKind::NonAsm => { - diag.span_label(span, "non-asm is unsupported in naked functions"); - } + let mut diag = struct_span_err!( + tcx.sess, + fn_span, + E0787, + "naked functions must contain a single asm block" + ); + let mut has_asm = false; + for &(kind, span) in &this.items { + match kind { + ItemKind::Asm if has_asm => { + diag.span_label(span, "multiple asm blocks are unsupported in naked functions"); + } + ItemKind::Asm => has_asm = true, + ItemKind::NonAsm => { + diag.span_label(span, "non-asm is unsupported in naked functions"); } } - diag.emit(); - }); + } + diag.emit(); } } @@ -233,23 +219,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> { ExprKind::InlineAsm(ref asm) => { self.items.push((ItemKind::Asm, span)); - self.check_inline_asm(expr.hir_id, asm, span); - } - - ExprKind::LlvmInlineAsm(..) => { - self.items.push((ItemKind::Asm, span)); - self.tcx.struct_span_lint_hir( - UNSUPPORTED_NAKED_FUNCTIONS, - expr.hir_id, - span, - |lint| { - lint.build( - "the LLVM-style inline assembly is unsupported in naked functions", - ) - .help("use the new asm! syntax specified in RFC 2873") - .emit(); - }, - ); + self.check_inline_asm(asm, span); } ExprKind::DropTemps(..) | ExprKind::Block(..) | ExprKind::Err => { @@ -258,7 +228,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> { } } - fn check_inline_asm(&self, hir_id: HirId, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) { + fn check_inline_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) { let unsupported_operands: Vec = asm .operands .iter() @@ -271,18 +241,17 @@ impl<'tcx> CheckInlineAssembly<'tcx> { }) .collect(); if !unsupported_operands.is_empty() { - self.tcx.struct_span_lint_hir( - UNSUPPORTED_NAKED_FUNCTIONS, - hir_id, + struct_span_err!( + self.tcx.sess, unsupported_operands, - |lint| { - lint.build("only `const` and `sym` operands are supported in naked functions") - .emit(); - }, - ); + E0787, + "only `const` and `sym` operands are supported in naked functions", + ) + .emit(); } let unsupported_options: Vec<&'static str> = [ + (InlineAsmOptions::MAY_UNWIND, "`may_unwind`"), (InlineAsmOptions::NOMEM, "`nomem`"), (InlineAsmOptions::NOSTACK, "`nostack`"), (InlineAsmOptions::PRESERVES_FLAGS, "`preserves_flags`"), @@ -294,30 +263,29 @@ impl<'tcx> CheckInlineAssembly<'tcx> { .collect(); if !unsupported_options.is_empty() { - self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| { - lint.build(&format!( - "asm options unsupported in naked functions: {}", - unsupported_options.join(", ") - )) - .emit(); - }); + struct_span_err!( + self.tcx.sess, + span, + E0787, + "asm options unsupported in naked functions: {}", + unsupported_options.join(", ") + ) + .emit(); } if !asm.options.contains(InlineAsmOptions::NORETURN) { - self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| { - lint.build("asm in naked functions must use `noreturn` option").emit(); - }); + struct_span_err!( + self.tcx.sess, + span, + E0787, + "asm in naked functions must use `noreturn` option" + ) + .emit(); } } } impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> { - type Map = ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { match stmt.kind { StmtKind::Item(..) => {} diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 707e6b123d..6cd9dc2328 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::Node; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; @@ -52,7 +52,7 @@ fn method_might_be_inlined( return true; } } - match tcx.hir().find(tcx.hir().local_def_id_to_hir_id(impl_src)) { + match tcx.hir().find_by_def_id(impl_src) { Some(Node::Item(item)) => item_might_be_inlined(tcx, &item, codegen_fn_attrs), Some(..) | None => span_bug!(impl_item.span, "impl did is not an item"), } @@ -74,12 +74,6 @@ struct ReachableContext<'tcx> { } impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_nested_body(&mut self, body: hir::BodyId) { let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.tcx.typeck_body(body)); @@ -140,14 +134,11 @@ impl<'tcx> ReachableContext<'tcx> { // Returns true if the given def ID represents a local item that is // eligible for inlining and false otherwise. fn def_id_represents_local_inlined_item(&self, def_id: DefId) -> bool { - let hir_id = match def_id.as_local() { - Some(def_id) => self.tcx.hir().local_def_id_to_hir_id(def_id), - None => { - return false; - } + let Some(def_id) = def_id.as_local() else { + return false; }; - match self.tcx.hir().find(hir_id) { + match self.tcx.hir().find_by_def_id(def_id) { Some(Node::Item(item)) => match item.kind { hir::ItemKind::Fn(..) => { item_might_be_inlined(self.tcx, &item, self.tcx.codegen_fn_attrs(def_id)) @@ -169,7 +160,8 @@ impl<'tcx> ReachableContext<'tcx> { if generics.requires_monomorphization(self.tcx) || attrs.requests_inline() { true } else { - let impl_did = self.tcx.hir().get_parent_did(hir_id); + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let impl_did = self.tcx.hir().get_parent_item(hir_id); // Check the impl. If the generics on the self // type of the impl require inlining, this method // does too. @@ -198,9 +190,7 @@ impl<'tcx> ReachableContext<'tcx> { continue; } - if let Some(ref item) = - self.tcx.hir().find(self.tcx.hir().local_def_id_to_hir_id(search_item)) - { + if let Some(ref item) = self.tcx.hir().find_by_def_id(search_item) { self.propagate_node(item, search_item); } } diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs index 8968c16398..fdf93e5893 100644 --- a/compiler/rustc_passes/src/region.rs +++ b/compiler/rustc_passes/src/region.rs @@ -10,7 +10,7 @@ use rustc_ast::walk_list; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Block, Expr, Local, Pat, PatKind, Stmt}; use rustc_index::vec::Idx; use rustc_middle::middle::region::*; @@ -366,7 +366,8 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h let target_scopes = visitor.fixup_scopes.drain(start_point..); for scope in target_scopes { - let mut yield_data = visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap(); + let mut yield_data = + visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap().last_mut().unwrap(); let count = yield_data.expr_and_pat_count; let span = yield_data.span; @@ -429,7 +430,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h }; let data = YieldData { span, expr_and_pat_count: visitor.expr_and_pat_count, source: *source }; - visitor.scope_tree.yield_in_scope.insert(scope, data); + match visitor.scope_tree.yield_in_scope.get_mut(&scope) { + Some(yields) => yields.push(data), + None => { + visitor.scope_tree.yield_in_scope.insert(scope, vec![data]); + } + } + if visitor.pessimistic_yield { debug!("resolve_expr in pessimistic_yield - marking scope {:?} for fixup", scope); visitor.fixup_scopes.push(scope); @@ -721,12 +728,6 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_block(&mut self, b: &'tcx Block<'tcx>) { resolve_block(self, b); } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 5f19991f9c..136059677c 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -9,9 +9,9 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{FieldDef, Generics, HirId, Item, TraitRef, Ty, TyKind, Variant}; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability::{DeprecationEntry, Index}; use rustc_middle::ty::{self, query::Providers, TyCtxt}; @@ -378,10 +378,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { /// Because stability levels are scoped lexically, we want to walk /// nested items in the context of the outer item, so enable /// deep-walking. - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, i: &'tcx Item<'tcx>) { @@ -577,26 +577,30 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { } fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) { - let stab_map = self.tcx.stability(); - let stab = stab_map.local_stability(def_id); - if stab.map_or(false, |stab| stab.level.is_stable()) { - let const_stab = stab_map.local_const_stability(def_id); - if const_stab.is_none() { - self.tcx.sess.span_err( - span, - "`#[stable]` const functions must also be either \ - `#[rustc_const_stable]` or `#[rustc_const_unstable]`", - ); - } + if !self.tcx.features().staged_api { + return; + } + + let is_const = self.tcx.is_const_fn(def_id.to_def_id()); + let is_stable = self + .tcx + .lookup_stability(def_id) + .map_or(false, |stability| stability.level.is_stable()); + let missing_const_stability_attribute = self.tcx.lookup_const_stability(def_id).is_none(); + let is_reachable = self.access_levels.is_reachable(def_id); + + if is_const && is_stable && missing_const_stability_attribute && is_reachable { + let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); + self.tcx.sess.span_err(span, &format!("{descr} has missing const stability attribute")); } } } impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, i: &'tcx Item<'tcx>) { @@ -612,13 +616,8 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { self.check_missing_stability(i.def_id, i.span); } - // Ensure `const fn` that are `stable` have one of `rustc_const_unstable` or - // `rustc_const_stable`. - if self.tcx.features().staged_api - && matches!(&i.kind, hir::ItemKind::Fn(sig, ..) if sig.header.is_const()) - { - self.check_missing_const_stability(i.def_id, i.span); - } + // Ensure stable `const fn` have a const stability attribute. + self.check_missing_const_stability(i.def_id, i.span); intravisit::walk_item(self, i) } @@ -629,9 +628,10 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { } fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { - let impl_def_id = self.tcx.hir().local_def_id(self.tcx.hir().get_parent_item(ii.hir_id())); + let impl_def_id = self.tcx.hir().get_parent_item(ii.hir_id()); if self.tcx.impl_trait_ref(impl_def_id).is_none() { self.check_missing_stability(ii.def_id, ii.span); + self.check_missing_const_stability(ii.def_id, ii.span); } intravisit::walk_impl_item(self, ii); } @@ -738,13 +738,13 @@ struct Checker<'tcx> { } impl<'tcx> Visitor<'tcx> for Checker<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; /// Because stability levels are scoped lexically, we want to walk /// nested items in the context of the outer item, so enable /// deep-walking. - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { @@ -794,19 +794,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - if let Res::Def(DefKind::Trait, trait_did) = t.path.res { - for impl_item_ref in items { - let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); - let trait_item_def_id = self - .tcx - .associated_items(trait_did) - .filter_by_name_unhygienic(impl_item.ident.name) - .next() - .map(|item| item.def_id); - if let Some(def_id) = trait_item_def_id { - // Pass `None` to skip deprecation warnings. - self.tcx.check_stability(def_id, None, impl_item.span, None); - } + for impl_item_ref in items { + let impl_item = self.tcx.associated_item(impl_item_ref.id.def_id); + + if let Some(def_id) = impl_item.trait_item_def_id { + // Pass `None` to skip deprecation warnings. + self.tcx.check_stability(def_id, None, impl_item_ref.span, None); } } } @@ -867,12 +860,6 @@ struct CheckTraitImplStable<'tcx> { } impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { - type Map = Map<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _id: hir::HirId) { if let Some(def_id) = path.res.opt_def_id() { if let Some(stab) = self.tcx.lookup_stability(def_id) { diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs index 2d84c8caad..25fe8e4582 100644 --- a/compiler/rustc_passes/src/upvars.rs +++ b/compiler/rustc_passes/src/upvars.rs @@ -3,7 +3,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self, HirId}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; @@ -43,12 +43,6 @@ struct LocalCollector { } impl<'tcx> Visitor<'tcx> for LocalCollector { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind { self.locals.insert(hir_id); @@ -72,12 +66,6 @@ impl CaptureCollector<'_, '_> { } impl<'tcx> Visitor<'tcx> for CaptureCollector<'_, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) { if let Res::Local(var_id) = path.res { self.visit_local_use(var_id, path.span); diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 61c82f031d..6b73c95011 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -1,10 +1,9 @@ //! Validity checking for weak lang items -use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::lang_items::{self, LangItem}; use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS; use rustc_middle::middle::lang_items::required; @@ -96,16 +95,9 @@ impl<'a, 'tcx> Context<'a, 'tcx> { } impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) { - let check_name = |attr: &Attribute, sym| attr.has_name(sym); let attrs = self.tcx.hir().attrs(i.hir_id()); - if let Some((lang_item, _)) = lang_items::extract(check_name, attrs) { + if let Some((lang_item, _)) = lang_items::extract(attrs) { self.register(lang_item, i.span); } intravisit::walk_foreign_item(self, i) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 183a5a205e..48594e73f5 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -4,6 +4,7 @@ #![feature(try_blocks)] #![feature(associated_type_defaults)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] use rustc_ast::MacroDef; use rustc_attr as attr; @@ -11,12 +12,11 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet}; -use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, DeepVisitor, NestedVisitorMap, Visitor}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; +use rustc_hir::intravisit::{self, DeepVisitor, Visitor}; use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind}; use rustc_middle::bug; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; use rustc_middle::span_bug; use rustc_middle::thir::abstract_const::Node as ACNode; @@ -26,7 +26,7 @@ use rustc_middle::ty::subst::InternalSubsts; 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, sym, Ident}; +use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst}; @@ -128,8 +128,8 @@ where constness: _, polarity: _, }) => self.visit_trait(trait_ref), - ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { - ty.visit_with(self)?; + ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => { + term.visit_with(self)?; self.visit_projection_ty(projection_ty) } ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => { @@ -178,10 +178,6 @@ where { type BreakTy = V::BreakTy; - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.def_id_visitor.tcx()) - } - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { let tcx = self.def_id_visitor.tcx(); // InternalSubsts are not visited here because they are visited below in `super_visit_with`. @@ -284,8 +280,8 @@ where } } - fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow { - self.visit_ty(c.ty)?; + fn visit_const(&mut self, c: 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)?; @@ -310,10 +306,10 @@ struct PubRestrictedVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for PubRestrictedVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_vis(&mut self, vis: &'tcx hir::Visibility<'tcx>) { self.has_pub_restricted = self.has_pub_restricted || vis.node.is_pub_restricted(); @@ -436,6 +432,15 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.access_levels.map.get(&def_id).copied() } + fn update_with_hir_id( + &mut self, + hir_id: hir::HirId, + level: Option, + ) -> Option { + let def_id = self.tcx.hir().local_def_id(hir_id); + self.update(def_id, level) + } + /// Updates node level and returns the updated level. fn update(&mut self, def_id: LocalDefId, level: Option) -> Option { let old_level = self.get(def_id); @@ -520,7 +525,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { let vis = self.tcx.visibility(item_id.def_id); self.update_macro_reachable_def(item_id.def_id, def_kind, vis, defining_mod); } - if let Some(exports) = self.tcx.module_exports(module_def_id) { + if let Some(exports) = self.tcx.module_reexports(module_def_id) { for export in exports { if export.vis.is_accessible_from(defining_mod.to_def_id(), self.tcx) { if let Res::Def(def_kind, def_id) = export.res { @@ -623,116 +628,37 @@ impl<'tcx> EmbargoVisitor<'tcx> { | DefKind::Generator => (), } } - - /// Given the path segments of an `ItemKind::Use`, then we need - /// to update the visibility of the intermediate use so that it isn't linted - /// by `unreachable_pub`. - /// - /// This isn't trivial as `path.res` has the `DefId` of the eventual target - /// of the use statement not of the next intermediate use statement. - /// - /// To do this, consider the last two segments of the path to our intermediate - /// use statement. We expect the penultimate segment to be a module and the - /// last segment to be the name of the item we are exporting. We can then - /// look at the items contained in the module for the use statement with that - /// name and update that item's visibility. - /// - /// FIXME: This solution won't work with glob imports and doesn't respect - /// namespaces. See . - fn update_visibility_of_intermediate_use_statements( - &mut self, - segments: &[hir::PathSegment<'_>], - ) { - if let [.., module, segment] = segments { - if let Some(item) = module - .res - .and_then(|res| res.mod_def_id()) - // If the module is `self`, i.e. the current crate, - // there will be no corresponding item. - .filter(|def_id| def_id.index != CRATE_DEF_INDEX || def_id.krate != LOCAL_CRATE) - .and_then(|def_id| def_id.as_local()) - .map(|module_hir_id| self.tcx.hir().expect_item(module_hir_id)) - { - if let hir::ItemKind::Mod(m) = &item.kind { - for &item_id in m.item_ids { - let item = self.tcx.hir().item(item_id); - if !self.tcx.hygienic_eq( - segment.ident, - item.ident, - item_id.def_id.to_def_id(), - ) { - continue; - } - if let hir::ItemKind::Use(..) = item.kind { - self.update(item.def_id, Some(AccessLevel::Exported)); - } - } - } - } - } - } } impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; /// We want to visit items in the context of their containing /// module and so forth, so supply a crate for doing a deep walk. - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - let inherited_item_level = match item.kind { + let item_level = match item.kind { hir::ItemKind::Impl { .. } => { - Option::::of_impl(item.def_id, self.tcx, &self.access_levels) - } - // Only exported `macro_rules!` items are public, but they always are. - hir::ItemKind::Macro(MacroDef { macro_rules: true, .. }) => { - let def_id = item.def_id.to_def_id(); - let is_macro_export = self.tcx.has_attr(def_id, sym::macro_export); - if is_macro_export { Some(AccessLevel::Public) } else { None } - } - // Foreign modules inherit level from parents. - hir::ItemKind::ForeignMod { .. } => self.prev_level, - // Other `pub` items inherit levels from parents. - hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::ExternCrate(..) - | hir::ItemKind::GlobalAsm(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::OpaqueTy(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::Use(..) => { - if item.vis.node.is_pub() { - self.prev_level - } else { - None - } + let impl_level = + Option::::of_impl(item.def_id, self.tcx, &self.access_levels); + self.update(item.def_id, impl_level) } + _ => self.get(item.def_id), }; - // Update level of the item itself. - let item_level = self.update(item.def_id, inherited_item_level); - // Update levels of nested things. match item.kind { hir::ItemKind::Enum(ref def, _) => { for variant in def.variants { - let variant_level = - self.update(self.tcx.hir().local_def_id(variant.id), item_level); + let variant_level = self.update_with_hir_id(variant.id, item_level); if let Some(ctor_hir_id) = variant.data.ctor_hir_id() { - self.update(self.tcx.hir().local_def_id(ctor_hir_id), item_level); + self.update_with_hir_id(ctor_hir_id, item_level); } for field in variant.data.fields() { - self.update(self.tcx.hir().local_def_id(field.hir_id), variant_level); + self.update_with_hir_id(field.hir_id, variant_level); } } } @@ -752,11 +678,11 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => { if let Some(ctor_hir_id) = def.ctor_hir_id() { - self.update(self.tcx.hir().local_def_id(ctor_hir_id), item_level); + self.update_with_hir_id(ctor_hir_id, item_level); } for field in def.fields() { if field.vis.node.is_pub() { - self.update(self.tcx.hir().local_def_id(field.hir_id), item_level); + self.update_with_hir_id(field.hir_id, item_level); } } } @@ -789,14 +715,8 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { hir::ItemKind::Macro(..) | hir::ItemKind::ExternCrate(..) => {} // All nested items are checked by `visit_item`. hir::ItemKind::Mod(..) => {} - // Re-exports are handled in `visit_mod`. However, in order to avoid looping over - // all of the items of a mod in `visit_mod` looking for use statements, we handle - // making sure that intermediate use statements have their visibilities updated here. - hir::ItemKind::Use(path, _) => { - if item_level.is_some() { - self.update_visibility_of_intermediate_use_statements(path.segments.as_ref()); - } - } + // Handled in the access level of in rustc_resolve + hir::ItemKind::Use(..) => {} // The interface is empty. hir::ItemKind::GlobalAsm(..) => {} hir::ItemKind::OpaqueTy(..) => { @@ -920,27 +840,6 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { intravisit::walk_block(self, b); self.prev_level = orig_level; } - - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _sp: Span, id: hir::HirId) { - // This code is here instead of in visit_item so that the - // crate module gets processed as well. - if self.prev_level.is_some() { - let def_id = self.tcx.hir().local_def_id(id); - if let Some(exports) = self.tcx.module_exports(def_id) { - for export in exports.iter() { - if export.vis.is_public() { - if let Some(def_id) = export.res.opt_def_id() { - if let Some(def_id) = def_id.as_local() { - self.update(def_id, Some(AccessLevel::Exported)); - } - } - } - } - } - } - - intravisit::walk_mod(self, m, id); - } } impl ReachEverythingInTheInterfaceVisitor<'_, '_> { @@ -1045,7 +944,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did, hir_id).1; if !field.vis.is_accessible_from(def_id, self.tcx) { let label = if in_update_syntax { - format!("field `{}` is private", field.ident) + format!("field `{}` is private", field.name) } else { "private field".to_string() }; @@ -1055,7 +954,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { span, E0451, "field `{}` of {} `{}` is private", - field.ident, + field.name, def.variant_descr(), self.tcx.def_path_str(def.did) ) @@ -1066,12 +965,12 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; /// We want to visit items in the context of their containing /// module and so forth, so supply a crate for doing a deep walk. - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_mod(&mut self, _m: &'tcx hir::Mod<'tcx>, _s: Span, _n: hir::HirId) { @@ -1195,12 +1094,12 @@ impl<'tcx> TypePrivacyVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; /// We want to visit items in the context of their containing /// module and so forth, so supply a crate for doing a deep walk. - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_mod(&mut self, _m: &'tcx hir::Mod<'tcx>, _s: Span, _n: hir::HirId) { @@ -1250,19 +1149,11 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { if self.visit(ty).is_break() { return; } + } else { + // We don't do anything for const infers here. } } else { - let local_id = self.tcx.hir().local_def_id(inf.hir_id); - if let Some(did) = self.tcx.opt_const_param_of(local_id) { - if self.visit_def_id(did, "inferred", &"").is_break() { - return; - } - } - - // FIXME see above note for same issue. - if self.visit(rustc_typeck::hir_ty_to_ty(self.tcx, &inf.to_ty())).is_break() { - return; - } + bug!("visit_infer without typeck_results"); } intravisit::walk_inf(self, inf); } @@ -1287,10 +1178,10 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { } for (poly_predicate, _) in bounds.projection_bounds { - if self.visit(poly_predicate.skip_binder().ty).is_break() - || self - .visit_projection_ty(poly_predicate.skip_binder().projection_ty) - .is_break() + let pred = poly_predicate.skip_binder(); + let poly_pred_term = self.visit(pred.term); + if poly_pred_term.is_break() + || self.visit_projection_ty(pred.projection_ty).is_break() { return; } @@ -1313,9 +1204,9 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { return; } } - hir::ExprKind::MethodCall(_, span, _, _) => { + hir::ExprKind::MethodCall(segment, ..) => { // Method calls have to be checked specially. - self.span = span; + self.span = segment.ident.span; if let Some(def_id) = self.typeck_results().type_dependent_def_id(expr.hir_id) { if self.visit(self.tcx.type_of(def_id)).is_break() { return; @@ -1459,7 +1350,7 @@ struct ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> { impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { fn path_is_private_type(&self, path: &hir::Path<'_>) -> bool { let did = match path.res { - Res::PrimTy(..) | Res::SelfTy(..) | Res::Err => return false, + Res::PrimTy(..) | Res::SelfTy { .. } | Res::Err => return false, res => res.def_id(), }; @@ -1497,12 +1388,6 @@ impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { } impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) { match generic_arg { hir::GenericArg::Type(t) => self.visit_ty(t), @@ -1533,12 +1418,12 @@ impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a } impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; /// We want to visit items in the context of their containing /// module and so forth, so supply a crate for doing a deep walk. - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { @@ -1982,10 +1867,10 @@ impl<'tcx> PrivateItemsInPublicInterfacesVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { @@ -2122,7 +2007,7 @@ fn visibility(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Visibility { // Visibilities of trait impl items are inherited from their traits // and are not filled in resolve. Node::ImplItem(impl_item) => { - match tcx.hir().get(tcx.hir().get_parent_item(hir_id)) { + match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(hir_id)) { Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(tr), .. }), .. @@ -2166,11 +2051,12 @@ fn privacy_access_levels(tcx: TyCtxt<'_>, (): ()) -> &AccessLevels { // items which are reachable from external crates based on visibility. let mut visitor = EmbargoVisitor { tcx, - access_levels: Default::default(), + access_levels: tcx.resolutions(()).access_levels.clone(), macro_reachable: Default::default(), prev_level: Some(AccessLevel::Public), changed: false, }; + loop { tcx.hir().walk_toplevel_module(&mut visitor); if visitor.changed { @@ -2179,7 +2065,6 @@ fn privacy_access_levels(tcx: TyCtxt<'_>, (): ()) -> &AccessLevels { break; } } - visitor.update(CRATE_DEF_ID, Some(AccessLevel::Public)); tcx.arena.alloc(visitor.access_levels) } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index f984bb1872..f1899a6fb2 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -8,7 +8,7 @@ doctest = false [dependencies] measureme = "10.0.0" -rustc-rayon-core = "0.3.1" +rustc-rayon-core = "0.3.2" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs index 581a2bce2e..84de31a194 100644 --- a/compiler/rustc_query_impl/src/keys.rs +++ b/compiler/rustc_query_impl/src/keys.rs @@ -275,7 +275,7 @@ impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) { } } -impl<'tcx> Key for (&'tcx ty::Const<'tcx>, mir::Field) { +impl<'tcx> Key for (ty::Const<'tcx>, mir::Field) { #[inline(always)] fn query_crate_is_local(&self) -> bool { true @@ -345,7 +345,7 @@ impl<'tcx> Key for mir::ConstantKind<'tcx> { } } -impl<'tcx> Key for &'tcx ty::Const<'tcx> { +impl<'tcx> Key for ty::Const<'tcx> { #[inline(always)] fn query_crate_is_local(&self) -> bool { true diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index de9d425353..55e95e1a59 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -7,6 +7,7 @@ #![feature(once_cell)] #![feature(rustc_attrs)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_macros; @@ -14,6 +15,7 @@ extern crate rustc_macros; extern crate rustc_middle; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::{self, DepKindStruct, SerializedDepNodeIndex}; use rustc_middle::ty::query::{query_keys, query_storage, query_stored, query_values}; @@ -27,9 +29,6 @@ mod plumbing; pub use plumbing::QueryCtxt; use rustc_query_system::query::*; -mod stats; -pub use self::stats::print_stats; - mod keys; use keys::Key; diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 6a88e12353..06e276ab42 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -163,15 +163,12 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> { // Decode the *position* of the footer, which can be found in the // last 8 bytes of the file. decoder.set_position(data.len() - IntEncodedWithFixedSize::ENCODED_SIZE); - let footer_pos = IntEncodedWithFixedSize::decode(&mut decoder) - .expect("error while trying to decode footer position") - .0 as usize; + let footer_pos = IntEncodedWithFixedSize::decode(&mut decoder).0 as usize; // Decode the file footer, which contains all the lookup tables, etc. decoder.set_position(footer_pos); decode_tagged(&mut decoder, TAG_FILE_FOOTER) - .expect("error while trying to decode footer position") }; Self { @@ -372,7 +369,7 @@ impl<'sess> OnDiskCache<'sess> { dep_node_index: SerializedDepNodeIndex, ) -> QuerySideEffects { let side_effects: Option = - self.load_indexed(tcx, dep_node_index, &self.prev_side_effects_index, "side_effects"); + self.load_indexed(tcx, dep_node_index, &self.prev_side_effects_index); side_effects.unwrap_or_default() } @@ -398,7 +395,7 @@ impl<'sess> OnDiskCache<'sess> { where T: for<'a> Decodable>, { - self.load_indexed(tcx, dep_node_index, &self.query_result_index, "query result") + self.load_indexed(tcx, dep_node_index, &self.query_result_index) } /// Stores side effect emitted during computation of an anonymous query. @@ -423,17 +420,13 @@ impl<'sess> OnDiskCache<'sess> { tcx: TyCtxt<'tcx>, dep_node_index: SerializedDepNodeIndex, index: &FxHashMap, - debug_tag: &'static str, ) -> Option where T: for<'a> Decodable>, { let pos = index.get(&dep_node_index).cloned()?; - self.with_decoder(tcx, pos, |decoder| match decode_tagged(decoder, dep_node_index) { - Ok(v) => Some(v), - Err(e) => bug!("could not decode cached {}: {}", debug_tag, e), - }) + self.with_decoder(tcx, pos, |decoder| Some(decode_tagged(decoder, dep_node_index))) } fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>( @@ -535,7 +528,7 @@ impl<'a, 'tcx> DecoderWithPosition for CacheDecoder<'a, 'tcx> { // Decodes something that was encoded with `encode_tagged()` and verify that the // tag matches and the correct amount of bytes was read. -fn decode_tagged(decoder: &mut D, expected_tag: T) -> Result +fn decode_tagged(decoder: &mut D, expected_tag: T) -> V where T: Decodable + Eq + std::fmt::Debug, V: Decodable, @@ -543,15 +536,15 @@ where { let start_pos = decoder.position(); - let actual_tag = T::decode(decoder)?; + let actual_tag = T::decode(decoder); assert_eq!(actual_tag, expected_tag); - let value = V::decode(decoder)?; + let value = V::decode(decoder); let end_pos = decoder.position(); - let expected_len: u64 = Decodable::decode(decoder)?; + let expected_len: u64 = Decodable::decode(decoder); assert_eq!((end_pos - start_pos) as u64, expected_len); - Ok(value) + value } impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { @@ -572,26 +565,22 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { self.opaque.data[self.opaque.position()] } - fn cached_ty_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> + fn cached_ty_for_shorthand(&mut self, shorthand: usize, or_insert_with: F) -> Ty<'tcx> where - F: FnOnce(&mut Self) -> Result, Self::Error>, + F: FnOnce(&mut Self) -> Ty<'tcx>, { let tcx = self.tcx(); let cache_key = ty::CReaderCacheKey { cnum: None, pos: shorthand }; if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) { - return Ok(ty); + return ty; } - let ty = or_insert_with(self)?; + let ty = or_insert_with(self); // This may overwrite the entry, but it should overwrite with the same value. tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty); - Ok(ty) + ty } fn with_position(&mut self, pos: usize, f: F) -> R @@ -607,7 +596,7 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { r } - fn decode_alloc_id(&mut self) -> Result { + fn decode_alloc_id(&mut self) -> interpret::AllocId { let alloc_decoding_session = self.alloc_decoding_session; alloc_decoding_session.decode_alloc_id(self) } @@ -619,35 +608,35 @@ rustc_middle::implement_ty_decoder!(CacheDecoder<'a, 'tcx>); // 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 { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { Decodable::decode(&mut d.opaque) } } impl<'a, 'tcx> Decodable> for SyntaxContext { - fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result { + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { let syntax_contexts = decoder.syntax_contexts; rustc_span::hygiene::decode_syntax_context(decoder, decoder.hygiene_context, |this, id| { // This closure is invoked if we haven't already decoded the data for the `SyntaxContext` we are deserializing. // We look up the position of the associated `SyntaxData` and decode it. let pos = syntax_contexts.get(&id).unwrap(); this.with_position(pos.to_usize(), |decoder| { - let data: SyntaxContextData = decode_tagged(decoder, TAG_SYNTAX_CONTEXT)?; - Ok(data) + let data: SyntaxContextData = decode_tagged(decoder, TAG_SYNTAX_CONTEXT); + data }) }) } } impl<'a, 'tcx> Decodable> for ExpnId { - fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result { - let hash = ExpnHash::decode(decoder)?; + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { + let hash = ExpnHash::decode(decoder); if hash.is_root() { - return Ok(ExpnId::root()); + return ExpnId::root(); } if let Some(expn_id) = ExpnId::from_hash(hash) { - return Ok(expn_id); + return expn_id; } let krate = decoder.tcx.stable_crate_id_to_crate_num(hash.stable_crate_id()); @@ -660,7 +649,7 @@ impl<'a, 'tcx> Decodable> for ExpnId { .unwrap_or_else(|| panic!("Bad hash {:?} (map {:?})", hash, decoder.expn_data)); let data: ExpnData = decoder - .with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA))?; + .with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA)); let expn_id = rustc_span::hygiene::register_local_expn_id(data, hash); #[cfg(debug_assertions)] @@ -687,21 +676,21 @@ impl<'a, 'tcx> Decodable> for ExpnId { }; debug_assert_eq!(expn_id.krate, krate); - Ok(expn_id) + expn_id } } impl<'a, 'tcx> Decodable> for Span { - fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result { - let ctxt = SyntaxContext::decode(decoder)?; - let parent = Option::::decode(decoder)?; - let tag: u8 = Decodable::decode(decoder)?; + fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self { + let ctxt = SyntaxContext::decode(decoder); + let parent = Option::::decode(decoder); + let tag: u8 = Decodable::decode(decoder); if tag == TAG_PARTIAL_SPAN { - return Ok(Span::new(BytePos(0), BytePos(0), ctxt, parent)); + return Span::new(BytePos(0), BytePos(0), ctxt, parent); } else if tag == TAG_RELATIVE_SPAN { - let dlo = u32::decode(decoder)?; - let dto = u32::decode(decoder)?; + let dlo = u32::decode(decoder); + let dto = u32::decode(decoder); let enclosing = decoder.tcx.definitions_untracked().def_span(parent.unwrap()).data_untracked(); @@ -712,29 +701,29 @@ impl<'a, 'tcx> Decodable> for Span { parent, ); - return Ok(span); + return span; } else { debug_assert_eq!(tag, TAG_FULL_SPAN); } - let file_lo_index = SourceFileIndex::decode(decoder)?; - let line_lo = usize::decode(decoder)?; - let col_lo = BytePos::decode(decoder)?; - let len = BytePos::decode(decoder)?; + let file_lo_index = SourceFileIndex::decode(decoder); + let line_lo = usize::decode(decoder); + let col_lo = BytePos::decode(decoder); + let len = BytePos::decode(decoder); let file_lo = decoder.file_index_to_file(file_lo_index); let lo = file_lo.lines[line_lo - 1] + col_lo; let hi = lo + len; - Ok(Span::new(lo, hi, ctxt, parent)) + Span::new(lo, hi, ctxt, parent) } } impl<'a, 'tcx> Decodable> for CrateNum { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { - let stable_id = StableCrateId::decode(d)?; + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + let stable_id = StableCrateId::decode(d); let cnum = d.tcx.stable_crate_id_to_crate_num(stable_id); - Ok(cnum) + cnum } } @@ -743,8 +732,8 @@ impl<'a, 'tcx> Decodable> for CrateNum { // because we would not know how to transform the `DefIndex` to the current // context. impl<'a, 'tcx> Decodable> for DefIndex { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { - Err(d.error("trying to decode `DefIndex` outside the context of a `DefId`")) + fn decode(_d: &mut CacheDecoder<'a, 'tcx>) -> DefIndex { + panic!("trying to decode `DefIndex` outside the context of a `DefId`") } } @@ -752,21 +741,23 @@ impl<'a, 'tcx> Decodable> for DefIndex { // compilation sessions. We use the `DefPathHash`, which is stable across // sessions, to map the old `DefId` to the new one. impl<'a, 'tcx> Decodable> for DefId { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { // Load the `DefPathHash` which is was we encoded the `DefId` as. - let def_path_hash = DefPathHash::decode(d)?; + let def_path_hash = DefPathHash::decode(d); // Using the `DefPathHash`, we can lookup the new `DefId`. // Subtle: We only encode a `DefId` as part of a query result. // If we get to this point, then all of the query inputs were green, // which means that the definition with this hash is guaranteed to // still exist in the current compilation session. - Ok(d.tcx().def_path_hash_to_def_id(def_path_hash)) + d.tcx().def_path_hash_to_def_id(def_path_hash, &mut || { + panic!("Failed to convert DefPathHash {:?}", def_path_hash) + }) } } impl<'a, 'tcx> Decodable> for &'tcx FxHashSet { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } } @@ -774,31 +765,31 @@ impl<'a, 'tcx> Decodable> for &'tcx FxHashSet impl<'a, 'tcx> Decodable> for &'tcx IndexVec> { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } } impl<'a, 'tcx> Decodable> for &'tcx [thir::abstract_const::Node<'tcx>] { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } } impl<'a, 'tcx> Decodable> for &'tcx [(ty::Predicate<'tcx>, Span)] { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } } impl<'a, 'tcx> Decodable> for &'tcx [rustc_ast::InlineAsmTemplatePiece] { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } } impl<'a, 'tcx> Decodable> for &'tcx [Span] { - fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 6d76d09f61..ff9d32a677 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -3,7 +3,7 @@ //! manage the caches, and so forth. use crate::{on_disk_cache, Queries}; -use rustc_middle::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex}; +use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::TyCtxt; use rustc_query_system::dep_graph::HasDepContext; @@ -15,6 +15,7 @@ use rustc_errors::{Diagnostic, Handler}; use rustc_serialize::opaque; use std::any::Any; +use std::num::NonZeroU64; #[derive(Copy, Clone)] pub struct QueryCtxt<'tcx> { @@ -42,11 +43,20 @@ impl<'tcx> HasDepContext for QueryCtxt<'tcx> { } impl QueryContext for QueryCtxt<'_> { - fn current_query_job(&self) -> Option> { + fn next_job_id(&self) -> QueryJobId { + QueryJobId( + NonZeroU64::new( + self.queries.jobs.fetch_add(1, rustc_data_structures::sync::Ordering::Relaxed), + ) + .unwrap(), + ) + } + + fn current_query_job(&self) -> Option { tls::with_related_context(**self, |icx| icx.query) } - fn try_collect_active_jobs(&self) -> Option> { + fn try_collect_active_jobs(&self) -> Option { self.queries.try_collect_active_jobs(**self) } @@ -81,7 +91,7 @@ impl QueryContext for QueryCtxt<'_> { #[inline(always)] fn start_query( &self, - token: QueryJobId, + token: QueryJobId, diagnostics: Option<&Lock>>, compute: impl FnOnce() -> R, ) -> R { @@ -152,7 +162,7 @@ impl<'tcx> QueryCtxt<'tcx> { pub fn try_print_query_stack( self, - query: Option>, + query: Option, handler: &Handler, num_frames: Option, ) -> usize { @@ -299,7 +309,7 @@ macro_rules! define_queries { } #[allow(nonstandard_style)] - pub mod queries { + mod queries { use std::marker::PhantomData; $(pub struct $name<$tcx> { @@ -320,7 +330,7 @@ macro_rules! define_queries { type Cache = query_storage::$name<$tcx>; #[inline(always)] - fn query_state<'a>(tcx: QueryCtxt<$tcx>) -> &'a QueryState + fn query_state<'a>(tcx: QueryCtxt<$tcx>) -> &'a QueryState where QueryCtxt<$tcx>: 'a { &tcx.queries.$name @@ -353,7 +363,7 @@ macro_rules! define_queries { })* #[allow(nonstandard_style)] - pub mod query_callbacks { + mod query_callbacks { use super::*; use rustc_middle::dep_graph::DepNode; use rustc_middle::ty::query::query_keys; @@ -402,7 +412,7 @@ macro_rules! define_queries { } } - $(pub fn $name()-> DepKindStruct { + $(pub(crate) fn $name()-> DepKindStruct { let is_anon = is_anon!([$($modifiers)*]); let is_eval_always = is_eval_always!([$($modifiers)*]); @@ -471,10 +481,9 @@ macro_rules! define_queries_struct { pub on_disk_cache: Option>, - $($(#[$attr])* $name: QueryState< - crate::dep_graph::DepKind, - query_keys::$name<$tcx>, - >,)* + jobs: AtomicU64, + + $($(#[$attr])* $name: QueryState>,)* } impl<$tcx> Queries<$tcx> { @@ -487,6 +496,7 @@ macro_rules! define_queries_struct { local_providers: Box::new(local_providers), extern_providers: Box::new(extern_providers), on_disk_cache, + jobs: AtomicU64::new(1), $($name: Default::default()),* } } @@ -494,14 +504,13 @@ macro_rules! define_queries_struct { pub(crate) fn try_collect_active_jobs( &$tcx self, tcx: TyCtxt<$tcx>, - ) -> Option> { + ) -> Option { let tcx = QueryCtxt { tcx, queries: self }; let mut jobs = QueryMap::default(); $( self.$name.try_collect_active_jobs( tcx, - dep_graph::DepKind::$name, make_query::$name, &mut jobs, )?; diff --git a/compiler/rustc_query_impl/src/stats.rs b/compiler/rustc_query_impl/src/stats.rs deleted file mode 100644 index c3bbd51f3d..0000000000 --- a/compiler/rustc_query_impl/src/stats.rs +++ /dev/null @@ -1,112 +0,0 @@ -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_middle::ty::query::query_storage; -use rustc_middle::ty::TyCtxt; -use rustc_query_system::query::{QueryCache, QueryCacheStore}; - -use std::any::type_name; -use std::mem; - -trait KeyStats { - fn key_stats(&self, stats: &mut QueryStats); -} - -impl KeyStats for T { - default fn key_stats(&self, _: &mut QueryStats) {} -} - -impl KeyStats for DefId { - fn key_stats(&self, stats: &mut QueryStats) { - if self.krate == LOCAL_CRATE { - stats.local_def_id_keys = Some(stats.local_def_id_keys.unwrap_or(0) + 1); - } - } -} - -#[derive(Clone)] -struct QueryStats { - name: &'static str, - key_size: usize, - key_type: &'static str, - value_size: usize, - value_type: &'static str, - entry_count: usize, - local_def_id_keys: Option, -} - -fn stats(name: &'static str, map: &QueryCacheStore) -> QueryStats -where - C: QueryCache, -{ - let mut stats = QueryStats { - name, - key_size: mem::size_of::(), - key_type: type_name::(), - value_size: mem::size_of::(), - value_type: type_name::(), - entry_count: 0, - local_def_id_keys: None, - }; - map.iter_results(&mut |key, _, _| { - stats.entry_count += 1; - key.key_stats(&mut stats) - }); - stats -} - -pub fn print_stats(tcx: TyCtxt<'_>) { - let queries = query_stats(tcx); - - let mut query_key_sizes = queries.clone(); - query_key_sizes.sort_by_key(|q| q.key_size); - eprintln!("\nLarge query keys:"); - for q in query_key_sizes.iter().rev().filter(|q| q.key_size > 8) { - eprintln!(" {} - {} x {} - {}", q.name, q.key_size, q.entry_count, q.key_type); - } - - let mut query_value_sizes = queries.clone(); - query_value_sizes.sort_by_key(|q| q.value_size); - eprintln!("\nLarge query values:"); - for q in query_value_sizes.iter().rev().filter(|q| q.value_size > 8) { - eprintln!(" {} - {} x {} - {}", q.name, q.value_size, q.entry_count, q.value_type); - } - - let mut query_value_count = queries.clone(); - query_value_count.sort_by_key(|q| q.entry_count); - eprintln!("\nQuery value count:"); - for q in query_value_count.iter().rev() { - eprintln!(" {} - {}", q.name, q.entry_count); - } - - let mut def_id_density: Vec<_> = - queries.iter().filter(|q| q.local_def_id_keys.is_some()).collect(); - def_id_density.sort_by_key(|q| q.local_def_id_keys.unwrap()); - eprintln!("\nLocal DefId density:"); - let total = tcx.resolutions(()).definitions.def_index_count() as f64; - for q in def_id_density.iter().rev() { - let local = q.local_def_id_keys.unwrap(); - eprintln!(" {} - {} = ({}%)", q.name, local, (local as f64 * 100.0) / total); - } -} - -macro_rules! print_stats { - (<$tcx:tt> - $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($K:ty) -> $V:ty,)* - ) => { - fn query_stats(tcx: TyCtxt<'_>) -> Vec { - let mut queries = Vec::new(); - - $( - queries.push(stats::< - query_storage::$name<'_>, - >( - stringify!($name), - &tcx.query_caches.$name, - )); - )* - - queries - } - } -} - -rustc_query_append! { [print_stats!][<'tcx>] } diff --git a/compiler/rustc_query_impl/src/values.rs b/compiler/rustc_query_impl/src/values.rs index 003867beeb..718a2971c4 100644 --- a/compiler/rustc_query_impl/src/values.rs +++ b/compiler/rustc_query_impl/src/values.rs @@ -1,5 +1,5 @@ use super::QueryCtxt; -use rustc_middle::ty::{self, AdtSizedConstraint, Ty, TyS}; +use rustc_middle::ty::{self, AdtSizedConstraint, Ty}; pub(super) trait Value<'tcx>: Sized { fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> Self; @@ -12,7 +12,7 @@ impl<'tcx, T> Value<'tcx> for T { } } -impl<'tcx> Value<'tcx> for &'_ TyS<'_> { +impl<'tcx> Value<'tcx> for Ty<'_> { fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> Self { // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 898a8caa3c..79f791eb75 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" -rustc-rayon-core = "0.3.1" +rustc-rayon-core = "0.3.2" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 7bc3fd718e..a080b4a3e9 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -9,6 +9,7 @@ use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; use rustc_index::vec::IndexVec; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use smallvec::{smallvec, SmallVec}; +use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; use std::fmt::Debug; use std::hash::Hash; @@ -165,7 +166,11 @@ impl DepGraph { pub fn assert_ignored(&self) { if let Some(..) = self.data { K::read_deps(|task_deps| { - assert!(task_deps.is_none(), "expected no task dependency tracking"); + assert_matches!( + task_deps, + TaskDepsRef::Ignore, + "expected no task dependency tracking" + ); }) } } @@ -174,7 +179,60 @@ impl DepGraph { where OP: FnOnce() -> R, { - K::with_deps(None, op) + K::with_deps(TaskDepsRef::Ignore, op) + } + + /// Used to wrap the deserialization of a query result from disk, + /// This method enforces that no new `DepNodes` are created during + /// query result deserialization. + /// + /// Enforcing this makes the query dep graph simpler - all nodes + /// must be created during the query execution, and should be + /// created from inside the 'body' of a query (the implementation + /// provided by a particular compiler crate). + /// + /// Consider the case of three queries `A`, `B`, and `C`, where + /// `A` invokes `B` and `B` invokes `C`: + /// + /// `A -> B -> C` + /// + /// Suppose that decoding the result of query `B` required re-computing + /// the query `C`. If we did not create a fresh `TaskDeps` when + /// decoding `B`, we would still be using the `TaskDeps` for query `A` + /// (if we needed to re-execute `A`). This would cause us to create + /// a new edge `A -> C`. If this edge did not previously + /// exist in the `DepGraph`, then we could end up with a different + /// `DepGraph` at the end of compilation, even if there were no + /// meaningful changes to the overall program (e.g. a newline was added). + /// In addition, this edge might cause a subsequent compilation run + /// to try to force `C` before marking other necessary nodes green. If + /// `C` did not exist in the new compilation session, then we could + /// get an ICE. Normally, we would have tried (and failed) to mark + /// some other query green (e.g. `item_children`) which was used + /// to obtain `C`, which would prevent us from ever trying to force + /// a non-existent `D`. + /// + /// It might be possible to enforce that all `DepNode`s read during + /// deserialization already exist in the previous `DepGraph`. In + /// the above example, we would invoke `D` during the deserialization + /// of `B`. Since we correctly create a new `TaskDeps` from the decoding + /// of `B`, this would result in an edge `B -> D`. If that edge already + /// existed (with the same `DepPathHash`es), then it should be correct + /// to allow the invocation of the query to proceed during deserialization + /// of a query result. We would merely assert that the dep-graph fragment + /// that would have been added by invoking `C` while decoding `B` + /// is equivalent to the dep-graph fragment that we already instantiated for B + /// (at the point where we successfully marked B as green). + /// + /// However, this would require additional complexity + /// in the query infrastructure, and is not currently needed by the + /// decoding of any query results. Should the need arise in the future, + /// we should consider extending the query system with this functionality. + pub fn with_query_deserialization(&self, op: OP) -> R + where + OP: FnOnce() -> R, + { + K::with_deps(TaskDepsRef::Forbid, op) } /// Starts a new dep-graph task. Dep-graph tasks are specified @@ -259,7 +317,13 @@ impl DepGraph { phantom_data: PhantomData, })) }; - let result = K::with_deps(task_deps.as_ref(), || task(cx, arg)); + + let task_deps_ref = match &task_deps { + Some(deps) => TaskDepsRef::Allow(deps), + None => TaskDepsRef::Ignore, + }; + + let result = K::with_deps(task_deps_ref, || task(cx, arg)); let edges = task_deps.map_or_else(|| smallvec![], |lock| lock.into_inner().reads); let dcx = cx.dep_context(); @@ -312,7 +376,7 @@ impl DepGraph { if let Some(ref data) = self.data { let task_deps = Lock::new(TaskDeps::default()); - let result = K::with_deps(Some(&task_deps), op); + let result = K::with_deps(TaskDepsRef::Allow(&task_deps), op); let task_deps = task_deps.into_inner(); let task_deps = task_deps.reads; @@ -365,42 +429,47 @@ impl DepGraph { pub fn read_index(&self, dep_node_index: DepNodeIndex) { if let Some(ref data) = self.data { K::read_deps(|task_deps| { - if let Some(task_deps) = task_deps { - let mut task_deps = task_deps.lock(); - let task_deps = &mut *task_deps; - if cfg!(debug_assertions) { - data.current.total_read_count.fetch_add(1, Relaxed); + let mut task_deps = match task_deps { + TaskDepsRef::Allow(deps) => deps.lock(), + TaskDepsRef::Ignore => return, + TaskDepsRef::Forbid => { + panic!("Illegal read of: {:?}", dep_node_index) } + }; + let task_deps = &mut *task_deps; - // As long as we only have a low number of reads we can avoid doing a hash - // insert and potentially allocating/reallocating the hashmap - let new_read = if task_deps.reads.len() < TASK_DEPS_READS_CAP { - task_deps.reads.iter().all(|other| *other != dep_node_index) - } else { - task_deps.read_set.insert(dep_node_index) - }; - if new_read { - task_deps.reads.push(dep_node_index); - if task_deps.reads.len() == TASK_DEPS_READS_CAP { - // Fill `read_set` with what we have so far so we can use the hashset - // next time - task_deps.read_set.extend(task_deps.reads.iter().copied()); - } + if cfg!(debug_assertions) { + data.current.total_read_count.fetch_add(1, Relaxed); + } - #[cfg(debug_assertions)] - { - if let Some(target) = task_deps.node { - if let Some(ref forbidden_edge) = data.current.forbidden_edge { - let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; - if forbidden_edge.test(&src, &target) { - panic!("forbidden edge {:?} -> {:?} created", src, target) - } + // As long as we only have a low number of reads we can avoid doing a hash + // insert and potentially allocating/reallocating the hashmap + let new_read = if task_deps.reads.len() < TASK_DEPS_READS_CAP { + task_deps.reads.iter().all(|other| *other != dep_node_index) + } else { + task_deps.read_set.insert(dep_node_index) + }; + if new_read { + task_deps.reads.push(dep_node_index); + if task_deps.reads.len() == TASK_DEPS_READS_CAP { + // Fill `read_set` with what we have so far so we can use the hashset + // next time + task_deps.read_set.extend(task_deps.reads.iter().copied()); + } + + #[cfg(debug_assertions)] + { + if let Some(target) = task_deps.node { + if let Some(ref forbidden_edge) = data.current.forbidden_edge { + let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; + if forbidden_edge.test(&src, &target) { + panic!("forbidden edge {:?} -> {:?} created", src, target) } } } - } else if cfg!(debug_assertions) { - data.current.total_duplicate_read_count.fetch_add(1, Relaxed); } + } else if cfg!(debug_assertions) { + data.current.total_duplicate_read_count.fetch_add(1, Relaxed); } }) } @@ -1123,7 +1192,26 @@ impl CurrentDepGraph { const TASK_DEPS_READS_CAP: usize = 8; type EdgesVec = SmallVec<[DepNodeIndex; TASK_DEPS_READS_CAP]>; -pub struct TaskDeps { +#[derive(Debug, Clone, Copy)] +pub enum TaskDepsRef<'a, K: DepKind> { + /// New dependencies can be added to the + /// `TaskDeps`. This is used when executing a 'normal' query + /// (no `eval_always` modifier) + Allow(&'a Lock>), + /// New dependencies are ignored. This is used when + /// executing an `eval_always` query, since there's no + /// need to track dependencies for a query that's always + /// re-executed. This is also used for `dep_graph.with_ignore` + Ignore, + /// Any attempt to add new dependencies will cause a panic. + /// This is used when decoding a query result from disk, + /// to ensure that the decoding process doesn't itself + /// require the execution of any queries. + Forbid, +} + +#[derive(Debug)] +pub struct TaskDeps { #[cfg(debug_assertions)] node: Option>, reads: EdgesVec, @@ -1131,7 +1219,7 @@ pub struct TaskDeps { phantom_data: PhantomData>, } -impl Default for TaskDeps { +impl Default for TaskDeps { fn default() -> Self { Self { #[cfg(debug_assertions)] diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 047fc9f10c..5907ae309c 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -5,13 +5,14 @@ mod query; mod serialized; pub use dep_node::{DepNode, DepNodeParams, WorkProductId}; -pub use graph::{hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, WorkProduct}; +pub use graph::{ + hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef, WorkProduct, +}; pub use query::DepGraphQuery; pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use crate::ich::StableHashingContext; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_data_structures::sync::Lock; use rustc_serialize::{opaque::FileEncoder, Encodable}; use rustc_session::Session; @@ -90,12 +91,12 @@ pub trait DepKind: Copy + fmt::Debug + Eq + Hash + Send + Encodable fn debug_node(node: &DepNode, f: &mut fmt::Formatter<'_>) -> fmt::Result; /// Execute the operation with provided dependencies. - fn with_deps(deps: Option<&Lock>>, op: OP) -> R + fn with_deps(deps: TaskDepsRef<'_, Self>, op: OP) -> R where OP: FnOnce() -> R; /// Access dependencies from current implicit context. fn read_deps(op: OP) where - OP: for<'a> FnOnce(Option<&'a Lock>>); + OP: for<'a> FnOnce(TaskDepsRef<'a, Self>); } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 47197a1e49..c95dff13d6 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -100,7 +100,7 @@ impl<'a, K: DepKind + Decodable>> Decodable { #[instrument(level = "debug", skip(d))] - fn decode(d: &mut opaque::Decoder<'a>) -> Result, String> { + fn decode(d: &mut opaque::Decoder<'a>) -> SerializedDepGraph { let start_position = d.position(); // The last 16 bytes are the node count and edge count. @@ -108,8 +108,8 @@ impl<'a, K: DepKind + Decodable>> Decodable>> Decodable = d.read_struct_field("node", Decodable::decode)?; + let dep_node: DepNode = d.read_struct_field("node", Decodable::decode); let _i: SerializedDepNodeIndex = nodes.push(dep_node); debug_assert_eq!(_i.index(), _index); let fingerprint: Fingerprint = - d.read_struct_field("fingerprint", Decodable::decode)?; + d.read_struct_field("fingerprint", Decodable::decode); let _i: SerializedDepNodeIndex = fingerprints.push(fingerprint); debug_assert_eq!(_i.index(), _index); @@ -136,22 +136,21 @@ impl<'a, K: DepKind + Decodable>> Decodable = nodes.iter_enumerated().map(|(idx, &dep_node)| (dep_node, idx)).collect(); - Ok(SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data, index }) + SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data, index } } } @@ -183,7 +182,7 @@ impl EncoderState { total_edge_count: 0, total_node_count: 0, result: Ok(()), - stats: if record_stats { Some(FxHashMap::default()) } else { None }, + stats: record_stats.then(FxHashMap::default), } } diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 5f31fa04b8..76e21be17b 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -3,6 +3,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{HashingControls, NodeIdHashingMode}; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -26,20 +27,15 @@ fn compute_ignored_attr_names() -> FxHashSet { pub struct StableHashingContext<'a> { definitions: &'a Definitions, cstore: &'a dyn CrateStore, + // The value of `-Z incremental-ignore-spans`. + // This field should only be used by `debug_opts_incremental_ignore_span` + incremental_ignore_spans: bool, pub(super) body_resolver: BodyResolver<'a>, - hash_spans: bool, - pub(super) node_id_hashing_mode: NodeIdHashingMode, - // Very often, we are hashing something that does not need the // `CachingSourceMapView`, so we initialize it lazily. raw_source_map: &'a SourceMap, caching_source_map: Option>, -} - -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum NodeIdHashingMode { - Ignore, - HashDefPath, + pub(super) hashing_controls: HashingControls, } /// The `BodyResolver` allows mapping a `BodyId` to the corresponding `hir::Body`. @@ -70,10 +66,13 @@ impl<'a> StableHashingContext<'a> { body_resolver: BodyResolver::Forbidden, definitions, cstore, + incremental_ignore_spans: sess.opts.debugging_opts.incremental_ignore_spans, caching_source_map: None, raw_source_map: sess.source_map(), - hash_spans: hash_spans_initial, - node_id_hashing_mode: NodeIdHashingMode::HashDefPath, + hashing_controls: HashingControls { + hash_spans: hash_spans_initial, + node_id_hashing_mode: NodeIdHashingMode::HashDefPath, + }, } } @@ -133,10 +132,10 @@ impl<'a> StableHashingContext<'a> { #[inline] pub fn while_hashing_spans(&mut self, hash_spans: bool, f: F) { - let prev_hash_spans = self.hash_spans; - self.hash_spans = hash_spans; + let prev_hash_spans = self.hashing_controls.hash_spans; + self.hashing_controls.hash_spans = hash_spans; f(self); - self.hash_spans = prev_hash_spans; + self.hashing_controls.hash_spans = prev_hash_spans; } #[inline] @@ -145,10 +144,10 @@ impl<'a> StableHashingContext<'a> { mode: NodeIdHashingMode, f: F, ) { - let prev = self.node_id_hashing_mode; - self.node_id_hashing_mode = mode; + let prev = self.hashing_controls.node_id_hashing_mode; + self.hashing_controls.node_id_hashing_mode = mode; f(self); - self.node_id_hashing_mode = prev; + self.hashing_controls.node_id_hashing_mode = prev; } #[inline] @@ -183,6 +182,11 @@ impl<'a> StableHashingContext<'a> { } IGNORED_ATTRIBUTES.with(|attrs| attrs.contains(&name)) } + + #[inline] + pub fn hashing_controls(&self) -> HashingControls { + self.hashing_controls.clone() + } } impl<'a> HashStable> for ast::NodeId { @@ -195,7 +199,12 @@ impl<'a> HashStable> for ast::NodeId { impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { #[inline] fn hash_spans(&self) -> bool { - self.hash_spans + self.hashing_controls.hash_spans + } + + #[inline] + fn debug_opts_incremental_ignore_spans(&self) -> bool { + self.incremental_ignore_spans } #[inline] @@ -215,6 +224,11 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { self.source_map().span_data_to_lines_and_cols(span) } + + #[inline] + fn hashing_controls(&self) -> HashingControls { + self.hashing_controls.clone() + } } impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} diff --git a/compiler/rustc_query_system/src/ich/impls_hir.rs b/compiler/rustc_query_system/src/ich/impls_hir.rs index 3117140a5b..bf3cf6a48f 100644 --- a/compiler/rustc_query_system/src/ich/impls_hir.rs +++ b/compiler/rustc_query_system/src/ich/impls_hir.rs @@ -11,7 +11,7 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { #[inline] fn hash_hir_id(&mut self, hir_id: hir::HirId, hasher: &mut StableHasher) { let hcx = self; - match hcx.node_id_hashing_mode { + match hcx.hashing_controls.node_id_hashing_mode { NodeIdHashingMode::Ignore => { // Don't do anything. } @@ -89,12 +89,12 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { #[inline] fn hash_hir_item_like(&mut self, f: F) { - let prev_hash_node_ids = self.node_id_hashing_mode; - self.node_id_hashing_mode = NodeIdHashingMode::Ignore; + let prev_hash_node_ids = self.hashing_controls.node_id_hashing_mode; + self.hashing_controls.node_id_hashing_mode = NodeIdHashingMode::Ignore; f(self); - self.node_id_hashing_mode = prev_hash_node_ids; + self.hashing_controls.node_id_hashing_mode = prev_hash_node_ids; } #[inline] diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs index 54416902e5..c42fcc9c82 100644 --- a/compiler/rustc_query_system/src/ich/mod.rs +++ b/compiler/rustc_query_system/src/ich/mod.rs @@ -1,6 +1,7 @@ //! ICH - Incremental Compilation Hash -pub use self::hcx::{NodeIdHashingMode, StableHashingContext}; +pub use self::hcx::StableHashingContext; +pub use rustc_data_structures::stable_hasher::NodeIdHashingMode; use rustc_span::symbol::{sym, Symbol}; mod hcx; diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 0436e07e2d..750ac76a77 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -5,6 +5,7 @@ #![feature(let_else)] #![feature(min_specialization)] #![feature(extern_types)] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index d2b102b6f8..b1ff1e15a9 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -59,7 +59,7 @@ pub trait QueryDescription: QueryConfig { fn describe(tcx: CTX, key: Self::Key) -> String; // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_state<'a>(tcx: CTX) -> &'a QueryState + fn query_state<'a>(tcx: CTX) -> &'a QueryState where CTX: 'a; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index bd67303099..adf878a7f0 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -7,13 +7,11 @@ use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, Handler, Leve use rustc_session::Session; use rustc_span::Span; -use std::convert::TryFrom; use std::hash::Hash; -use std::num::NonZeroU32; +use std::num::NonZeroU64; #[cfg(parallel_compiler)] use { - crate::dep_graph::DepKind, parking_lot::{Condvar, Mutex}, rustc_data_structures::fx::FxHashSet, rustc_data_structures::sync::Lock, @@ -33,80 +31,57 @@ pub struct QueryInfo { pub query: QueryStackFrame, } -pub type QueryMap = FxHashMap, QueryJobInfo>; - -/// A value uniquely identifying an active query job within a shard in the query cache. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct QueryShardJobId(pub NonZeroU32); +pub type QueryMap = FxHashMap; /// A value uniquely identifying an active query job. #[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct QueryJobId { - /// Which job within a shard is this - pub job: QueryShardJobId, - - /// In which shard is this job - pub shard: u16, +pub struct QueryJobId(pub NonZeroU64); - /// What kind of query this job is. - pub kind: D, -} - -impl QueryJobId -where - D: Copy + Clone + Eq + Hash, -{ - pub fn new(job: QueryShardJobId, shard: usize, kind: D) -> Self { - QueryJobId { job, shard: u16::try_from(shard).unwrap(), kind } - } - - fn query(self, map: &QueryMap) -> QueryStackFrame { +impl QueryJobId { + fn query(self, map: &QueryMap) -> QueryStackFrame { map.get(&self).unwrap().query.clone() } #[cfg(parallel_compiler)] - fn span(self, map: &QueryMap) -> Span { + fn span(self, map: &QueryMap) -> Span { map.get(&self).unwrap().job.span } #[cfg(parallel_compiler)] - fn parent(self, map: &QueryMap) -> Option> { + fn parent(self, map: &QueryMap) -> Option { map.get(&self).unwrap().job.parent } #[cfg(parallel_compiler)] - fn latch<'a>(self, map: &'a QueryMap) -> Option<&'a QueryLatch> { + fn latch<'a>(self, map: &'a QueryMap) -> Option<&'a QueryLatch> { map.get(&self).unwrap().job.latch.as_ref() } } -pub struct QueryJobInfo { +pub struct QueryJobInfo { pub query: QueryStackFrame, - pub job: QueryJob, + pub job: QueryJob, } /// Represents an active query job. #[derive(Clone)] -pub struct QueryJob { - pub id: QueryShardJobId, +pub struct QueryJob { + pub id: QueryJobId, /// The span corresponding to the reason for which this query was required. pub span: Span, /// The parent query job which created this job and is implicitly waiting on it. - pub parent: Option>, + pub parent: Option, /// The latch that is used to wait on this job. #[cfg(parallel_compiler)] - latch: Option>, + latch: Option, } -impl QueryJob -where - D: Copy + Clone + Eq + Hash, -{ +impl QueryJob { /// Creates a new query job. - pub fn new(id: QueryShardJobId, span: Span, parent: Option>) -> Self { + pub fn new(id: QueryJobId, span: Span, parent: Option) -> Self { QueryJob { id, span, @@ -117,7 +92,7 @@ where } #[cfg(parallel_compiler)] - pub(super) fn latch(&mut self) -> QueryLatch { + pub(super) fn latch(&mut self) -> QueryLatch { if self.latch.is_none() { self.latch = Some(QueryLatch::new()); } @@ -139,16 +114,13 @@ where } #[cfg(not(parallel_compiler))] -impl QueryJobId -where - D: Copy + Clone + Eq + Hash, -{ +impl QueryJobId { #[cold] #[inline(never)] pub(super) fn find_cycle_in_stack( &self, - query_map: QueryMap, - current_job: &Option>, + query_map: QueryMap, + current_job: &Option, span: Span, ) -> CycleError { // Find the waitee amongst `current_job` parents @@ -184,15 +156,15 @@ where } #[cfg(parallel_compiler)] -struct QueryWaiter { - query: Option>, +struct QueryWaiter { + query: Option, condvar: Condvar, span: Span, cycle: Lock>, } #[cfg(parallel_compiler)] -impl QueryWaiter { +impl QueryWaiter { fn notify(&self, registry: &rayon_core::Registry) { rayon_core::mark_unblocked(registry); self.condvar.notify_one(); @@ -200,34 +172,27 @@ impl QueryWaiter { } #[cfg(parallel_compiler)] -struct QueryLatchInfo { +struct QueryLatchInfo { complete: bool, - waiters: Vec>>, + waiters: Vec>, } #[cfg(parallel_compiler)] #[derive(Clone)] -pub(super) struct QueryLatch { - info: Lrc>>, +pub(super) struct QueryLatch { + info: Lrc>, } #[cfg(parallel_compiler)] -impl QueryLatch { +impl QueryLatch { fn new() -> Self { QueryLatch { info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), } } -} -#[cfg(parallel_compiler)] -impl QueryLatch { /// Awaits for the query job to complete. - pub(super) fn wait_on( - &self, - query: Option>, - span: Span, - ) -> Result<(), CycleError> { + pub(super) fn wait_on(&self, query: Option, span: Span) -> Result<(), CycleError> { let waiter = Lrc::new(QueryWaiter { query, span, cycle: Lock::new(None), condvar: Condvar::new() }); self.wait_on_inner(&waiter); @@ -242,7 +207,7 @@ impl QueryLatch { } /// Awaits the caller on this latch by blocking the current thread. - fn wait_on_inner(&self, waiter: &Lrc>) { + fn wait_on_inner(&self, waiter: &Lrc) { let mut info = self.info.lock(); if !info.complete { // We push the waiter on to the `waiters` list. It can be accessed inside @@ -276,7 +241,7 @@ impl QueryLatch { /// Removes a single waiter from the list of waiters. /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Lrc> { + fn extract_waiter(&self, waiter: usize) -> Lrc { let mut info = self.info.lock(); debug_assert!(!info.complete); // Remove the waiter from the list of waiters @@ -286,7 +251,7 @@ impl QueryLatch { /// A resumable waiter of a query. The usize is the index into waiters in the query's latch #[cfg(parallel_compiler)] -type Waiter = (QueryJobId, usize); +type Waiter = (QueryJobId, usize); /// Visits all the non-resumable and resumable waiters of a query. /// Only waiters in a query are visited. @@ -298,14 +263,9 @@ type Waiter = (QueryJobId, usize); /// required information to resume the waiter. /// If all `visit` calls returns None, this function also returns None. #[cfg(parallel_compiler)] -fn visit_waiters( - query_map: &QueryMap, - query: QueryJobId, - mut visit: F, -) -> Option>> +fn visit_waiters(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option> where - D: Copy + Clone + Eq + Hash, - F: FnMut(Span, QueryJobId) -> Option>>, + F: FnMut(Span, QueryJobId) -> Option>, { // Visit the parent query which is a non-resumable waiter since it's on the same stack if let Some(parent) = query.parent(query_map) { @@ -334,16 +294,13 @@ where /// If a cycle is detected, this initial value is replaced with the span causing /// the cycle. #[cfg(parallel_compiler)] -fn cycle_check( - query_map: &QueryMap, - query: QueryJobId, +fn cycle_check( + query_map: &QueryMap, + query: QueryJobId, span: Span, - stack: &mut Vec<(Span, QueryJobId)>, - visited: &mut FxHashSet>, -) -> Option>> -where - D: Copy + Clone + Eq + Hash, -{ + stack: &mut Vec<(Span, QueryJobId)>, + visited: &mut FxHashSet, +) -> Option> { if !visited.insert(query) { return if let Some(p) = stack.iter().position(|q| q.1 == query) { // We detected a query cycle, fix up the initial span and return Some @@ -378,14 +335,11 @@ where /// from `query` without going through any of the queries in `visited`. /// This is achieved with a depth first search. #[cfg(parallel_compiler)] -fn connected_to_root( - query_map: &QueryMap, - query: QueryJobId, - visited: &mut FxHashSet>, -) -> bool -where - D: Copy + Clone + Eq + Hash, -{ +fn connected_to_root( + query_map: &QueryMap, + query: QueryJobId, + visited: &mut FxHashSet, +) -> bool { // We already visited this or we're deliberately ignoring it if !visited.insert(query) { return false; @@ -404,10 +358,9 @@ where // Deterministically pick an query from a list #[cfg(parallel_compiler)] -fn pick_query<'a, D, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T +fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T where - D: Copy + Clone + Eq + Hash, - F: Fn(&T) -> (Span, QueryJobId), + F: Fn(&T) -> (Span, QueryJobId), { // Deterministically pick an entry point // FIXME: Sort this instead @@ -431,10 +384,10 @@ where /// If a cycle was not found, the starting query is removed from `jobs` and /// the function returns false. #[cfg(parallel_compiler)] -fn remove_cycle( - query_map: &QueryMap, - jobs: &mut Vec>, - wakelist: &mut Vec>>, +fn remove_cycle( + query_map: &QueryMap, + jobs: &mut Vec, + wakelist: &mut Vec>, ) -> bool { let mut visited = FxHashSet::default(); let mut stack = Vec::new(); @@ -489,7 +442,7 @@ fn remove_cycle( } } }) - .collect::, Option<(Span, QueryJobId)>)>>(); + .collect::)>>(); // Deterministically pick an entry point let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1)); @@ -544,7 +497,7 @@ pub fn deadlock(tcx: CTX, registry: &rayon_core::Registry) { let mut wakelist = Vec::new(); let query_map = tcx.try_collect_active_jobs().unwrap(); - let mut jobs: Vec> = query_map.keys().cloned().collect(); + let mut jobs: Vec = query_map.keys().cloned().collect(); let mut found_cycle = false; @@ -630,7 +583,7 @@ pub(crate) fn report_cycle<'a>( pub fn print_query_stack( tcx: CTX, - mut current_query: Option>, + mut current_query: Option, handler: &Handler, num_frames: Option, ) -> usize { diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index a2f7843baa..361ae3c435 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -117,10 +117,12 @@ impl QuerySideEffects { } pub trait QueryContext: HasDepContext { + fn next_job_id(&self) -> QueryJobId; + /// Get the query information from the TLS context. - fn current_query_job(&self) -> Option>; + fn current_query_job(&self) -> Option; - fn try_collect_active_jobs(&self) -> Option>; + fn try_collect_active_jobs(&self) -> Option; /// Load side effects associated to the node in the previous session. fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects; @@ -140,7 +142,7 @@ pub trait QueryContext: HasDepContext { /// captured during execution and the actual result. fn start_query( &self, - token: QueryJobId, + token: QueryJobId, diagnostics: Option<&Lock>>, compute: impl FnOnce() -> R, ) -> R; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 33732f9df7..77e1fd3f2c 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -5,11 +5,8 @@ use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams}; use crate::query::caches::QueryCache; use crate::query::config::{QueryDescription, QueryVtable}; -use crate::query::job::{ - report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryShardJobId, -}; +use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo}; use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame}; - use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHasher}; #[cfg(parallel_compiler)] @@ -25,7 +22,6 @@ use std::collections::hash_map::Entry; use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::mem; -use std::num::NonZeroU32; use std::ptr; pub struct QueryCacheStore { @@ -70,36 +66,32 @@ impl QueryCacheStore { } } -struct QueryStateShard { - active: FxHashMap>, - - /// Used to generate unique ids for active jobs. - jobs: u32, +struct QueryStateShard { + active: FxHashMap, } -impl Default for QueryStateShard { - fn default() -> QueryStateShard { - QueryStateShard { active: Default::default(), jobs: 0 } +impl Default for QueryStateShard { + fn default() -> QueryStateShard { + QueryStateShard { active: Default::default() } } } -pub struct QueryState { - shards: Sharded>, +pub struct QueryState { + shards: Sharded>, } /// Indicates the state of a query for a given key in a query map. -enum QueryResult { +enum QueryResult { /// An already executing query. The query job can be used to await for its completion. - Started(QueryJob), + Started(QueryJob), /// The query panicked. Queries trying to wait on this will raise a fatal error which will /// silently panic. Poisoned, } -impl QueryState +impl QueryState where - D: Copy + Clone + Eq + Hash, K: Eq + Hash + Clone + Debug, { pub fn all_inactive(&self) -> bool { @@ -110,19 +102,17 @@ where pub fn try_collect_active_jobs( &self, tcx: CTX, - kind: D, make_query: fn(CTX, K) -> QueryStackFrame, - jobs: &mut QueryMap, + jobs: &mut QueryMap, ) -> Option<()> { // We use try_lock_shards here since we are called from the // deadlock handler, and this shouldn't be locked. let shards = self.shards.try_lock_shards()?; - for (shard_id, shard) in shards.iter().enumerate() { + for shard in shards.iter() { for (k, v) in shard.active.iter() { if let QueryResult::Started(ref job) = *v { - let id = QueryJobId::new(job.id, shard_id, kind); let query = make_query(tcx, k.clone()); - jobs.insert(id, QueryJobInfo { query, job: job.clone() }); + jobs.insert(job.id, QueryJobInfo { query, job: job.clone() }); } } } @@ -131,22 +121,21 @@ where } } -impl Default for QueryState { - fn default() -> QueryState { +impl Default for QueryState { + fn default() -> QueryState { QueryState { shards: Default::default() } } } /// A type representing the responsibility to execute the job in the `job` field. /// This will poison the relevant query if dropped. -struct JobOwner<'tcx, D, K> +struct JobOwner<'tcx, K> where - D: Copy + Clone + Eq + Hash, K: Eq + Hash + Clone, { - state: &'tcx QueryState, + state: &'tcx QueryState, key: K, - id: QueryJobId, + id: QueryJobId, } #[cold] @@ -167,9 +156,8 @@ where cache.store_nocache(value) } -impl<'tcx, D, K> JobOwner<'tcx, D, K> +impl<'tcx, K> JobOwner<'tcx, K> where - D: Copy + Clone + Eq + Hash, K: Eq + Hash + Clone, { /// Either gets a `JobOwner` corresponding the query, allowing us to @@ -183,12 +171,11 @@ where #[inline(always)] fn try_start<'b, CTX>( tcx: &'b CTX, - state: &'b QueryState, + state: &'b QueryState, span: Span, key: K, lookup: QueryLookup, - dep_kind: CTX::DepKind, - ) -> TryGetJob<'b, CTX::DepKind, K> + ) -> TryGetJob<'b, K> where CTX: QueryContext, { @@ -198,27 +185,21 @@ where match lock.active.entry(key) { Entry::Vacant(entry) => { - // Generate an id unique within this shard. - let id = lock.jobs.checked_add(1).unwrap(); - lock.jobs = id; - let id = QueryShardJobId(NonZeroU32::new(id).unwrap()); - + let id = tcx.next_job_id(); let job = tcx.current_query_job(); let job = QueryJob::new(id, span, job); let key = entry.key().clone(); entry.insert(QueryResult::Started(job)); - let global_id = QueryJobId::new(id, shard, dep_kind); - let owner = JobOwner { state, id: global_id, key }; + let owner = JobOwner { state, id, key }; return TryGetJob::NotYetStarted(owner); } Entry::Occupied(mut entry) => { match entry.get_mut() { #[cfg(not(parallel_compiler))] QueryResult::Started(job) => { - let id = QueryJobId::new(job.id, shard, dep_kind); - + let id = job.id; drop(state_lock); // If we are single-threaded we know that we have cycle error, @@ -296,9 +277,8 @@ where } } -impl<'tcx, D, K> Drop for JobOwner<'tcx, D, K> +impl<'tcx, K> Drop for JobOwner<'tcx, K> where - D: Copy + Clone + Eq + Hash, K: Eq + Hash + Clone, { #[inline(never)] @@ -330,13 +310,12 @@ pub(crate) struct CycleError { } /// The result of `try_start`. -enum TryGetJob<'tcx, D, K> +enum TryGetJob<'tcx, K> where - D: Copy + Clone + Eq + Hash, K: Eq + Hash + Clone, { /// The query is not yet started. Contains a guard to the cache eventually used to start it. - NotYetStarted(JobOwner<'tcx, D, K>), + NotYetStarted(JobOwner<'tcx, K>), /// The query was already completed. /// Returns the result of the query and its dep-node index @@ -376,7 +355,7 @@ where fn try_execute_query( tcx: CTX, - state: &QueryState, + state: &QueryState, cache: &QueryCacheStore, span: Span, key: C::Key, @@ -389,14 +368,7 @@ where C::Key: Clone + DepNodeParams, CTX: QueryContext, { - match JobOwner::<'_, CTX::DepKind, C::Key>::try_start( - &tcx, - state, - span, - key.clone(), - lookup, - query.dep_kind, - ) { + match JobOwner::<'_, C::Key>::try_start(&tcx, state, span, key.clone(), lookup) { TryGetJob::NotYetStarted(job) => { let (result, dep_node_index) = execute_job(tcx, key, dep_node, query, job.id); let result = job.complete(cache, result, dep_node_index); @@ -428,7 +400,7 @@ fn execute_job( key: K, mut dep_node_opt: Option>, query: &QueryVtable, - job_id: QueryJobId, + job_id: QueryJobId, ) -> (V, DepNodeIndex) where K: Clone + DepNodeParams, @@ -515,7 +487,13 @@ where // Some things are never cached on disk. if query.cache_on_disk { let prof_timer = tcx.dep_context().profiler().incr_cache_loading(); - let result = query.try_load_from_disk(tcx, prev_dep_node_index); + + // The call to `with_query_deserialization` enforces that no new `DepNodes` + // are created during deserialization. See the docs of that method for more + // details. + let result = dep_graph + .with_query_deserialization(|| query.try_load_from_disk(tcx, prev_dep_node_index)); + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); if let Some(result) = result { diff --git a/compiler/rustc_resolve/src/access_levels.rs b/compiler/rustc_resolve/src/access_levels.rs new file mode 100644 index 0000000000..60cc4248ed --- /dev/null +++ b/compiler/rustc_resolve/src/access_levels.rs @@ -0,0 +1,237 @@ +use rustc_ast::ast; +use rustc_ast::visit; +use rustc_ast::visit::Visitor; +use rustc_ast::Crate; +use rustc_ast::EnumDef; +use rustc_ast::ForeignMod; +use rustc_ast::NodeId; +use rustc_ast_lowering::ResolverAstLowering; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::CRATE_DEF_ID; +use rustc_middle::middle::privacy::AccessLevel; +use rustc_middle::ty::Visibility; +use rustc_span::sym; + +use crate::imports::ImportKind; +use crate::BindingKey; +use crate::NameBinding; +use crate::NameBindingKind; +use crate::Resolver; + +pub struct AccessLevelsVisitor<'r, 'a> { + r: &'r mut Resolver<'a>, + prev_level: Option, + changed: bool, +} + +impl<'r, 'a> AccessLevelsVisitor<'r, 'a> { + /// Fills the `Resolver::access_levels` table with public & exported items + /// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we + /// need access to a TyCtxt for that. + pub fn compute_access_levels<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) { + let mut visitor = + AccessLevelsVisitor { r, changed: false, prev_level: Some(AccessLevel::Public) }; + + visitor.set_access_level_def_id(CRATE_DEF_ID, Some(AccessLevel::Public)); + visitor.set_exports_access_level(CRATE_DEF_ID); + + while visitor.changed { + visitor.reset(); + visit::walk_crate(&mut visitor, krate); + } + + tracing::info!("resolve::access_levels: {:#?}", r.access_levels); + } + + fn reset(&mut self) { + self.changed = false; + self.prev_level = Some(AccessLevel::Public); + } + + /// Update the access level of the exports of the given module accordingly. The module access + /// level has to be Exported or Public. + /// This will also follow `use` chains (see PrivacyVisitor::set_import_binding_access_level). + fn set_exports_access_level(&mut self, module_id: LocalDefId) { + assert!(self.r.module_map.contains_key(&&module_id.to_def_id())); + + // Set the given binding access level to `AccessLevel::Public` and + // sets the rest of the `use` chain to `AccessLevel::Exported` until + // we hit the actual exported item. + let set_import_binding_access_level = + |this: &mut Self, mut binding: &NameBinding<'a>, mut access_level| { + while let NameBindingKind::Import { binding: nested_binding, import, .. } = + binding.kind + { + this.set_access_level(import.id, access_level); + if let ImportKind::Single { additional_ids, .. } = import.kind { + this.set_access_level(additional_ids.0, access_level); + this.set_access_level(additional_ids.1, access_level); + } + + access_level = Some(AccessLevel::Exported); + binding = nested_binding; + } + }; + + let module_level = self.r.access_levels.map.get(&module_id).copied(); + assert!(module_level >= Some(AccessLevel::Exported)); + + if let Some(exports) = self.r.reexport_map.get(&module_id) { + let pub_exports = exports + .iter() + .filter(|ex| ex.vis == Visibility::Public) + .cloned() + .collect::>(); + + let module = self.r.get_module(module_id.to_def_id()).unwrap(); + for export in pub_exports.into_iter() { + if let Some(export_def_id) = export.res.opt_def_id().and_then(|id| id.as_local()) { + self.set_access_level_def_id(export_def_id, Some(AccessLevel::Exported)); + } + + if let Some(ns) = export.res.ns() { + let key = BindingKey { ident: export.ident, ns, disambiguator: 0 }; + let name_res = self.r.resolution(module, key); + if let Some(binding) = name_res.borrow().binding() { + set_import_binding_access_level(self, binding, module_level) + } + } + } + } + } + + /// Sets the access level of the `LocalDefId` corresponding to the given `NodeId`. + /// This function will panic if the `NodeId` does not have a `LocalDefId` + fn set_access_level( + &mut self, + node_id: NodeId, + access_level: Option, + ) -> Option { + self.set_access_level_def_id(self.r.local_def_id(node_id), access_level) + } + + fn set_access_level_def_id( + &mut self, + def_id: LocalDefId, + access_level: Option, + ) -> Option { + let old_level = self.r.access_levels.map.get(&def_id).copied(); + if old_level < access_level { + self.r.access_levels.map.insert(def_id, access_level.unwrap()); + self.changed = true; + access_level + } else { + old_level + } + } +} + +impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> { + fn visit_item(&mut self, item: &'ast ast::Item) { + let inherited_item_level = match item.kind { + // Resolved in rustc_privacy when types are available + ast::ItemKind::Impl(..) => return, + + // Only exported `macro_rules!` items are public, but they always are + ast::ItemKind::MacroDef(..) => { + let is_macro_export = + item.attrs.iter().any(|attr| attr.has_name(sym::macro_export)); + if is_macro_export { Some(AccessLevel::Public) } else { None } + } + + // Foreign modules inherit level from parents. + ast::ItemKind::ForeignMod(..) => self.prev_level, + + // Other `pub` items inherit levels from parents. + ast::ItemKind::ExternCrate(..) + | ast::ItemKind::Use(..) + | ast::ItemKind::Static(..) + | ast::ItemKind::Const(..) + | ast::ItemKind::Fn(..) + | ast::ItemKind::Mod(..) + | ast::ItemKind::GlobalAsm(..) + | ast::ItemKind::TyAlias(..) + | ast::ItemKind::Enum(..) + | ast::ItemKind::Struct(..) + | ast::ItemKind::Union(..) + | ast::ItemKind::Trait(..) + | ast::ItemKind::TraitAlias(..) => { + if item.vis.kind.is_pub() { + self.prev_level + } else { + None + } + } + + // Should be unreachable at this stage + ast::ItemKind::MacCall(..) => panic!( + "ast::ItemKind::MacCall encountered, this should not anymore appear at this stage" + ), + }; + + let access_level = self.set_access_level(item.id, inherited_item_level); + + // Set access level of nested items. + // If it's a mod, also make the visitor walk all of its items + match item.kind { + ast::ItemKind::Mod(..) => { + if access_level.is_some() { + self.set_exports_access_level(self.r.local_def_id(item.id)); + } + + let orig_level = std::mem::replace(&mut self.prev_level, access_level); + visit::walk_item(self, item); + self.prev_level = orig_level; + } + + ast::ItemKind::ForeignMod(ForeignMod { ref items, .. }) => { + for nested in items { + if nested.vis.kind.is_pub() { + self.set_access_level(nested.id, access_level); + } + } + } + ast::ItemKind::Enum(EnumDef { ref variants }, _) => { + for variant in variants { + let variant_level = self.set_access_level(variant.id, access_level); + if let Some(ctor_id) = variant.data.ctor_id() { + self.set_access_level(ctor_id, access_level); + } + + for field in variant.data.fields() { + self.set_access_level(field.id, variant_level); + } + } + } + ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => { + if let Some(ctor_id) = def.ctor_id() { + self.set_access_level(ctor_id, access_level); + } + + for field in def.fields() { + if field.vis.kind.is_pub() { + self.set_access_level(field.id, access_level); + } + } + } + ast::ItemKind::Trait(ref trait_kind) => { + for nested in trait_kind.items.iter() { + self.set_access_level(nested.id, access_level); + } + } + + ast::ItemKind::ExternCrate(..) + | ast::ItemKind::Use(..) + | ast::ItemKind::Static(..) + | ast::ItemKind::Const(..) + | ast::ItemKind::GlobalAsm(..) + | ast::ItemKind::TyAlias(..) + | ast::ItemKind::TraitAlias(..) + | ast::ItemKind::MacroDef(..) + | ast::ItemKind::Fn(..) => return, + + // Unreachable kinds + ast::ItemKind::Impl(..) | ast::ItemKind::MacCall(..) => unreachable!(), + } + } +} diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 39074f811a..3fa9343c39 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -26,7 +26,7 @@ use rustc_hir::def::{self, *}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX}; use rustc_metadata::creader::LoadedMacro; use rustc_middle::bug; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::ty; use rustc_session::cstore::CrateStore; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; @@ -108,7 +108,7 @@ impl<'a> Resolver<'a> { /// Reachable macros with block module parents exist due to `#[macro_export] macro_rules!`, /// but they cannot use def-site hygiene, so the assumption holds /// (). - crate fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'a> { + pub fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'a> { loop { match self.get_module(def_id) { Some(module) => return module, @@ -214,7 +214,7 @@ impl<'a> Resolver<'a> { } crate fn build_reduced_graph_external(&mut self, module: Module<'a>) { - for child in self.cstore().item_children_untracked(module.def_id(), self.session) { + for child in self.cstore().module_children_untracked(module.def_id(), self.session) { let parent_scope = ParentScope::module(module, self); BuildReducedGraphVisitor { r: self, parent_scope } .build_reduced_graph_for_external_crate_res(child); @@ -383,8 +383,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { used: Cell::new(false), }); - debug!("add_import({:?})", import); - self.r.indeterminate_imports.push(import); match import.kind { // Don't add unresolved underscore imports to modules @@ -455,7 +453,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { prefix.is_empty() || prefix.len() == 1 && prefix[0].ident.name == kw::PathRoot }; match use_tree.kind { - ast::UseTreeKind::Simple(rename, ..) => { + ast::UseTreeKind::Simple(rename, id1, id2) => { let mut ident = use_tree.ident(); let mut module_path = prefix; let mut source = module_path.pop().unwrap(); @@ -565,7 +563,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }, type_ns_only, nested, + additional_ids: (id1, id2), }; + self.add_import( module_path, kind, @@ -938,9 +938,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } /// Builds the reduced graph for a single item in an external crate. - fn build_reduced_graph_for_external_crate_res(&mut self, child: Export) { + fn build_reduced_graph_for_external_crate_res(&mut self, child: ModChild) { let parent = self.parent_scope.module; - let Export { ident, res, vis, span } = child; + let ModChild { ident, res, vis, span } = child; let res = res.expect_non_local(); let expansion = self.parent_scope.expansion; // Record primary definitions. @@ -991,7 +991,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _, ) | Res::Local(..) - | Res::SelfTy(..) + | Res::SelfTy { .. } | Res::SelfCtor(..) | Res::Err => bug!("unexpected resolution: {:?}", res), } @@ -999,12 +999,14 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let cstore = self.r.cstore(); match res { Res::Def(DefKind::Struct, def_id) => { - let field_names = cstore.struct_field_names_untracked(def_id, self.r.session); + let field_names = + cstore.struct_field_names_untracked(def_id, self.r.session).collect(); let ctor = cstore.ctor_def_id_and_kind_untracked(def_id); if let Some((ctor_def_id, ctor_kind)) = ctor { let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); let ctor_vis = cstore.visibility_untracked(ctor_def_id); - let field_visibilities = cstore.struct_field_visibilities_untracked(def_id); + let field_visibilities = + cstore.struct_field_visibilities_untracked(def_id).collect(); self.r .struct_constructors .insert(def_id, (ctor_res, ctor_vis, field_visibilities)); @@ -1012,7 +1014,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.insert_field_names(def_id, field_names); } Res::Def(DefKind::Union, def_id) => { - let field_names = cstore.struct_field_names_untracked(def_id, self.r.session); + let field_names = + cstore.struct_field_names_untracked(def_id, self.r.session).collect(); self.insert_field_names(def_id, field_names); } Res::Def(DefKind::AssocFn, def_id) => { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 4feeae5cab..9a2fb3b86e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -123,7 +123,7 @@ impl<'a> Resolver<'a> { let sm = self.session.source_map(); match outer_res { - Res::SelfTy(maybe_trait_defid, maybe_impl_defid) => { + Res::SelfTy { trait_: maybe_trait_defid, alias_to: maybe_impl_defid } => { if let Some(impl_span) = maybe_impl_defid.and_then(|(def_id, _)| self.opt_span(def_id)) { @@ -453,28 +453,28 @@ impl<'a> Resolver<'a> { // edit: // only do this if the const and usage of the non-constant value are on the same line // the further the two are apart, the higher the chance of the suggestion being wrong - // also make sure that the pos for the suggestion is not 0 (ICE #90878) - let sp = - self.session.source_map().span_extend_to_prev_str(ident.span, current, true); - - let pos_for_suggestion = sp.lo().0.saturating_sub(current.len() as u32); + let sp = self + .session + .source_map() + .span_extend_to_prev_str(ident.span, current, true, false); - if sp.lo().0 == 0 - || pos_for_suggestion == 0 - || self.session.source_map().is_multiline(sp) - { - err.span_label(ident.span, &format!("this would need to be a `{}`", sugg)); - } else { - let sp = sp.with_lo(BytePos(pos_for_suggestion)); - err.span_suggestion( - sp, - &format!("consider using `{}` instead of `{}`", sugg, current), - format!("{} {}", sugg, ident), - Applicability::MaybeIncorrect, - ); - err.span_label(span, "non-constant value"); + match sp { + Some(sp) if !self.session.source_map().is_multiline(sp) => { + 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.span_label(ident.span, &format!("this would need to be a `{}`", sugg)); + } } + err } ResolutionError::BindingShadowsSomethingUnacceptable { @@ -602,6 +602,25 @@ impl<'a> Resolver<'a> { err } + ResolutionError::TraitImplMismatch { + name, + kind, + code, + trait_item_span, + trait_path, + } => { + let mut err = self.session.struct_span_err_with_code( + span, + &format!( + "item `{}` is an associated {}, which doesn't match its trait `{}`", + name, kind, trait_path, + ), + code, + ); + err.span_label(span, "does not match trait"); + err.span_label(trait_item_span, "item in trait"); + err + } } } @@ -895,11 +914,8 @@ impl<'a> Resolver<'a> { // a note about editions let note = if let Some(did) = did { let requires_note = !did.is_local() - && this - .cstore() - .item_attrs_untracked(did, this.session) - .iter() - .any(|attr| { + && this.cstore().item_attrs_untracked(did, this.session).any( + |attr| { if attr.has_name(sym::rustc_diagnostic_item) { [sym::TryInto, sym::TryFrom, sym::FromIterator] .map(|x| Some(x)) @@ -907,7 +923,8 @@ impl<'a> Resolver<'a> { } else { false } - }); + }, + ); requires_note.then(|| { format!( @@ -1345,8 +1362,7 @@ impl<'a> Resolver<'a> { .filter(|(_, module)| { current_module.is_ancestor_of(module) && !ptr::eq(current_module, *module) }) - .map(|(_, module)| module.kind.name()) - .flatten(), + .flat_map(|(_, module)| module.kind.name()), ) .filter(|c| !c.to_string().is_empty()) .collect::>(); @@ -1842,7 +1858,7 @@ crate fn show_candidates( let instead = if instead { " instead" } else { "" }; let mut msg = format!("consider importing {} {}{}", determiner, kind, instead); - for note in accessible_path_strings.iter().map(|cand| cand.3.as_ref()).flatten() { + for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { err.note(note); } @@ -1925,7 +1941,7 @@ crate fn show_candidates( multi_span.push_span_label(span, format!("`{}`: not accessible", name)); } - for note in inaccessible_path_strings.iter().map(|cand| cand.3.as_ref()).flatten() { + for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { err.note(note); } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index bf4cece8bd..a8c2a5e142 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -11,11 +11,11 @@ use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBindin use rustc_ast::NodeId; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::ptr_key::PtrKey; +use rustc_data_structures::intern::Interned; use rustc_errors::{pluralize, struct_span_err, Applicability}; use rustc_hir::def::{self, PartialRes}; use rustc_hir::def_id::DefId; -use rustc_middle::hir::exports::Export; +use rustc_middle::metadata::ModChild; use rustc_middle::span_bug; use rustc_middle::ty; use rustc_session::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS}; @@ -48,6 +48,9 @@ pub enum ImportKind<'a> { type_ns_only: bool, /// Did this import result from a nested import? ie. `use foo::{bar, baz};` nested: bool, + /// Additional `NodeId`s allocated to a `ast::UseTree` for automatically generated `use` statement + /// (eg. implicit struct constructors) + additional_ids: (NodeId, NodeId), }, Glob { is_prelude: bool, @@ -131,7 +134,7 @@ impl<'a> Import<'a> { pub struct NameResolution<'a> { /// Single imports that may define the name in the namespace. /// Imports are arena-allocated, so it's ok to use pointers as keys. - single_imports: FxHashSet>>, + single_imports: FxHashSet>>, /// The least shadowable known binding for this name, or None if there are no known bindings. pub binding: Option<&'a NameBinding<'a>>, shadowed_glob: Option<&'a NameBinding<'a>>, @@ -150,7 +153,7 @@ impl<'a> NameResolution<'a> { } crate fn add_single_import(&mut self, import: &'a Import<'a>) { - self.single_imports.insert(PtrKey(import)); + self.single_imports.insert(Interned::new_unchecked(import)); } } @@ -834,7 +837,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> { import.span, ); import.vis.set(orig_vis); - source_bindings[ns].set(binding); } else { return; @@ -848,7 +850,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { Err(Determined) => { let key = this.new_key(target, ns); this.update_resolution(parent, key, |_, resolution| { - resolution.single_imports.remove(&PtrKey(import)); + resolution.single_imports.remove(&Interned::new_unchecked(import)); }); } Ok(binding) if !binding.is_importable() => { @@ -1409,7 +1411,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if is_good_import || binding.is_macro_def() { let res = binding.res().expect_non_local(); if res != def::Res::Err { - reexports.push(Export { ident, res, span: binding.span, vis: binding.vis }); + reexports.push(ModChild { ident, res, vis: binding.vis, span: binding.span }); } } }); @@ -1418,7 +1420,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if let Some(def_id) = module.opt_def_id() { // Call to `expect_local` should be fine because current // code is only called for local modules. - self.r.export_map.insert(def_id.expect_local(), reexports); + self.r.reexport_map.insert(def_id.expect_local(), reexports); } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5730502313..9ac3e6e22b 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -289,7 +289,7 @@ impl<'a> PathSource<'a> { | DefKind::ForeignTy, _, ) | Res::PrimTy(..) - | Res::SelfTy(..) + | Res::SelfTy { .. } ), PathSource::Trait(AliasPossibility::No) => matches!(res, Res::Def(DefKind::Trait, _)), PathSource::Trait(AliasPossibility::Maybe) => { @@ -326,7 +326,7 @@ impl<'a> PathSource<'a> { | DefKind::TyAlias | DefKind::AssocTy, _, - ) | Res::SelfTy(..) + ) | Res::SelfTy { .. } ), PathSource::TraitItem(ns) => match res { Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true, @@ -400,6 +400,8 @@ struct DiagnosticMetadata<'ast> { /// Given `where ::Baz: String`, suggest `where T: Bar`. current_where_predicate: Option<&'ast WherePredicate>, + + current_type_path: Option<&'ast Ty>, } struct LateResolutionVisitor<'a, 'b, 'ast> { @@ -472,8 +474,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } fn visit_ty(&mut self, ty: &'ast Ty) { let prev = self.diagnostic_metadata.current_trait_object; + let prev_ty = self.diagnostic_metadata.current_type_path; match ty.kind { TyKind::Path(ref qself, ref path) => { + self.diagnostic_metadata.current_type_path = Some(ty); self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type); } TyKind::ImplicitSelf => { @@ -490,6 +494,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } visit::walk_ty(self, ty); self.diagnostic_metadata.current_trait_object = prev; + self.diagnostic_metadata.current_type_path = prev_ty; } fn visit_poly_trait_ref(&mut self, tref: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) { self.smart_resolve_path( @@ -520,9 +525,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, _: NodeId) { let rib_kind = match fn_kind { - // Bail if there's no body. - FnKind::Fn(.., None) => return visit::walk_fn(self, fn_kind, sp), - FnKind::Fn(FnCtxt::Free | FnCtxt::Foreign, ..) => FnItemRibKind, + // Bail if the function is foreign, and thus cannot validly have + // a body, or if there's no body for some other reason. + FnKind::Fn(FnCtxt::Foreign, _, sig, ..) | FnKind::Fn(_, _, sig, .., None) => { + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + self.visit_fn_header(&sig.header); + visit::walk_fn_decl(self, &sig.decl); + return; + } + FnKind::Fn(FnCtxt::Free, ..) => FnItemRibKind, FnKind::Fn(FnCtxt::Assoc(_), ..) => NormalRibKind, FnKind::Closure(..) => ClosureOrAsyncRibKind, }; @@ -899,9 +911,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.with_current_self_item(item, |this| { this.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { let item_def_id = this.r.local_def_id(item.id).to_def_id(); - this.with_self_rib(Res::SelfTy(None, Some((item_def_id, false))), |this| { - visit::walk_item(this, item); - }); + this.with_self_rib( + Res::SelfTy { trait_: None, alias_to: Some((item_def_id, false)) }, + |this| { + visit::walk_item(this, item); + }, + ); }); }); } @@ -987,8 +1002,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.compute_num_lifetime_params(item.id, generics); // 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(); - this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| { + let def = this.r.local_def_id(item.id).to_def_id(); + this.with_self_rib(Res::SelfTy { trait_: Some(def), alias_to: None }, |this| { this.visit_generics(generics); walk_list!(this, visit_param_bound, bounds); @@ -1039,8 +1054,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.compute_num_lifetime_params(item.id, generics); // 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(); - this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| { + let def = this.r.local_def_id(item.id).to_def_id(); + this.with_self_rib(Res::SelfTy { trait_: Some(def), alias_to: None }, |this| { this.visit_generics(generics); walk_list!(this, visit_param_bound, bounds); }); @@ -1240,15 +1255,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ); let res = res.base_res(); if res != Res::Err { - new_id = Some(res.def_id()); - let span = trait_ref.path.span; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path( &path, Some(TypeNS), - false, - span, + true, + trait_ref.path.span, CrateLint::SimplePath(trait_ref.ref_id), ) { + new_id = Some(res.def_id()); new_val = Some((module, trait_ref.clone())); } } @@ -1285,7 +1299,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // If applicable, create a rib for the type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { // Dummy self type for better errors if `Self` is used in the trait path. - this.with_self_rib(Res::SelfTy(None, None), |this| { + this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| { // Resolve the trait reference, if necessary. this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| { let item_def_id = this.r.local_def_id(item_id); @@ -1296,7 +1310,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } let item_def_id = item_def_id.to_def_id(); - this.with_self_rib(Res::SelfTy(trait_id, Some((item_def_id, false))), |this| { + let res = + Res::SelfTy { trait_: trait_id, alias_to: Some((item_def_id, false)) }; + this.with_self_rib(res, |this| { if let Some(trait_ref) = opt_trait_reference.as_ref() { // Resolve type arguments in the trait path. visit::walk_trait_ref(this, trait_ref); @@ -1317,6 +1333,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // If this is a trait impl, ensure the const // exists in trait this.check_trait_item( + item.id, item.ident, &item.kind, ValueNS, @@ -1352,6 +1369,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // If this is a trait impl, ensure the method // exists in trait this.check_trait_item( + item.id, item.ident, &item.kind, ValueNS, @@ -1379,6 +1397,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // If this is a trait impl, ensure the type // exists in trait this.check_trait_item( + item.id, item.ident, &item.kind, TypeNS, @@ -1409,7 +1428,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn check_trait_item( &mut self, - ident: Ident, + id: NodeId, + mut ident: Ident, kind: &AssocItemKind, ns: Namespace, span: Span, @@ -1417,26 +1437,62 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ) where F: FnOnce(Ident, &str, Option) -> ResolutionError<'_>, { - // If there is a TraitRef in scope for an impl, then the method must be in the - // 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, - span, - ) - .is_err() - { - let candidate = self.find_similarly_named_assoc_item(ident.name, kind); - let path = &self.current_trait_ref.as_ref().unwrap().1.path; - self.report_error(span, err(ident, &path_names_to_string(path), candidate)); + // If there is a TraitRef in scope for an impl, then the method must be in the trait. + let Some((module, _)) = &self.current_trait_ref else { return; }; + ident.span.normalize_to_macros_2_0_and_adjust(module.expansion); + let key = self.r.new_key(ident, ns); + let mut binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding); + debug!(?binding); + if binding.is_none() { + // We could not find the trait item in the correct namespace. + // Check the other namespace to report an error. + let ns = match ns { + ValueNS => TypeNS, + TypeNS => ValueNS, + _ => ns, + }; + let key = self.r.new_key(ident, ns); + binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding); + debug!(?binding); + } + let Some(binding) = binding else { + // We could not find the method: report an error. + let candidate = self.find_similarly_named_assoc_item(ident.name, kind); + let path = &self.current_trait_ref.as_ref().unwrap().1.path; + self.report_error(span, err(ident, &path_names_to_string(path), candidate)); + return; + }; + + let res = binding.res(); + let Res::Def(def_kind, _) = res else { bug!() }; + match (def_kind, kind) { + (DefKind::AssocTy, AssocItemKind::TyAlias(..)) + | (DefKind::AssocFn, AssocItemKind::Fn(..)) + | (DefKind::AssocConst, AssocItemKind::Const(..)) => { + self.r.record_partial_res(id, PartialRes::new(res)); + return; } + _ => {} } + + // The method kind does not correspond to what appeared in the trait, report. + let path = &self.current_trait_ref.as_ref().unwrap().1.path; + let (code, kind) = match kind { + AssocItemKind::Const(..) => (rustc_errors::error_code!(E0323), "const"), + AssocItemKind::Fn(..) => (rustc_errors::error_code!(E0324), "method"), + AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"), + AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"), + }; + self.report_error( + span, + ResolutionError::TraitImplMismatch { + name: ident.name, + kind, + code, + trait_path: path_names_to_string(path), + trait_item_span: binding.span, + }, + ); } fn resolve_params(&mut self, params: &'ast [Param]) { @@ -1890,7 +1946,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let instead = res.is_some(); let suggestion = if res.is_none() { this.report_missing_type_error(path) } else { None }; - // get_from_node_id this.r.use_injections.push(UseError { err, @@ -2471,6 +2526,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.visit_expr(elem); self.resolve_anon_const(ct, IsRepeatExpr::Yes); } + ExprKind::Index(ref elem, ref idx) => { + self.resolve_expr(elem, Some(expr)); + self.visit_expr(idx); + } _ => { visit::walk_expr(self, expr); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 4cd1b34bed..129db038f4 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -26,6 +26,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use std::iter; +use std::ops::Deref; use tracing::debug; @@ -265,6 +266,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } + self.detect_assoct_type_constraint_meant_as_path(base_span, &mut err); + // Emit special messages for unresolved `Self` and `self`. if is_self_type(path, ns) { err.code(rustc_errors::error_code!(E0411)); @@ -603,6 +606,40 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { (err, candidates) } + fn detect_assoct_type_constraint_meant_as_path( + &self, + base_span: Span, + err: &mut DiagnosticBuilder<'_>, + ) { + let Some(ty) = self.diagnostic_metadata.current_type_path else { return; }; + let TyKind::Path(_, path) = &ty.kind else { return; }; + for segment in &path.segments { + let Some(params) = &segment.args else { continue; }; + let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; }; + for param in ¶ms.args { + let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; }; + let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else { + continue; + }; + for bound in bounds { + let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None) + = bound else + { + continue; + }; + if base_span == trait_ref.span { + err.span_suggestion_verbose( + constraint.ident.span.between(trait_ref.span), + "you might have meant to write a path instead of an associated type bound", + "::".to_string(), + Applicability::MachineApplicable, + ); + } + } + } + } + } + fn get_single_associated_item( &mut self, path: &[Segment], @@ -667,7 +704,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ) = &bounded_ty.kind { // use this to verify that ident is a type param. - let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( + let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( bounded_ty.id, None, &Segment::from_path(path), @@ -675,9 +712,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { span, true, CrateLint::No, - ) { - partial_res - } else { + ) else { return false; }; if !(matches!( @@ -694,7 +729,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if let ast::TyKind::Path(None, type_param_path) = &ty.peel_refs().kind { // Confirm that the `SelfTy` is a type parameter. - let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( + let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( bounded_ty.id, None, &Segment::from_path(type_param_path), @@ -702,9 +737,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { span, true, CrateLint::No, - ) { - partial_res - } else { + ) else { return false; }; if !(matches!( @@ -970,7 +1003,13 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { }; match (res, source) { - (Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => { + ( + Res::Def(DefKind::Macro(MacroKind::Bang), _), + PathSource::Expr(Some(Expr { + kind: ExprKind::Index(..) | ExprKind::Call(..), .. + })) + | PathSource::Struct, + ) => { err.span_label(span, fallback_label); err.span_suggestion_verbose( span.shrink_to_hi(), @@ -982,6 +1021,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.note("if you want the `try` keyword, you need Rust 2018 or later"); } } + (Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => { + err.span_label(span, fallback_label); + } (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => { err.span_label(span, "type aliases cannot be used as traits"); if self.r.session.is_nightly_build() { @@ -1121,7 +1163,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestion( span, &"use this syntax instead", - format!("{path_str}"), + path_str.to_string(), Applicability::MaybeIncorrect, ); } @@ -1143,7 +1185,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { Applicability::HasPlaceholders, ); } - (Res::SelfTy(..), _) if ns == ValueNS => { + (Res::SelfTy { .. }, _) if ns == ValueNS => { err.span_label(span, fallback_label); err.note("can't use `Self` as a constructor, you must use the implemented struct"); } @@ -1162,9 +1204,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ident: Symbol, kind: &AssocItemKind, ) -> Option { - let module = if let Some((module, _)) = self.current_trait_ref { - module - } else { + let Some((module, _)) = &self.current_trait_ref else { return None; }; if ident == kw::Underscore { diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 3322e36502..3bea95fa1d 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -14,10 +14,11 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefIdMap, LocalDefId}; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath}; use rustc_hir::{GenericParamKind, HirIdMap, HirIdSet, LifetimeParamKind}; use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -40,9 +41,9 @@ pub enum LifetimeUseSet<'tcx> { } trait RegionExt { - fn early(hir_map: &Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region); + fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region); - fn late(index: u32, hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region); + fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region); fn late_anon(named_late_bound_vars: u32, index: &Cell) -> Region; @@ -58,7 +59,7 @@ trait RegionExt { } impl RegionExt for Region { - fn early(hir_map: &Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region) { + fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region) { let i = *index; *index += 1; let def_id = hir_map.local_def_id(param.hir_id); @@ -67,7 +68,7 @@ impl RegionExt for Region { (param.name.normalize_to_macros_2_0(), Region::EarlyBound(i, def_id.to_def_id(), origin)) } - fn late(idx: u32, hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region) { + fn late(idx: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region) { let depth = ty::INNERMOST; let def_id = hir_map.local_def_id(param.hir_id); let origin = LifetimeDefOrigin::from_param(param); @@ -376,12 +377,9 @@ pub fn provide(providers: &mut ty::query::Providers) { named_region_map: |tcx, id| resolve_lifetimes_for(tcx, id).defs.get(&id), is_late_bound_map, - object_lifetime_defaults_map: |tcx, id| { - let hir_id = tcx.hir().local_def_id_to_hir_id(id); - match tcx.hir().find(hir_id) { - Some(Node::Item(item)) => compute_object_lifetime_defaults(tcx, item), - _ => None, - } + object_lifetime_defaults: |tcx, id| match tcx.hir().find_by_def_id(id) { + Some(Node::Item(item)) => compute_object_lifetime_defaults(tcx, item), + _ => None, }, late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id), lifetime_scope_map: |tcx, id| { @@ -514,14 +512,14 @@ fn resolve_lifetimes_for<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx R /// Finds the `Item` that contains the given `LocalDefId` fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> LocalDefId { - let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id); - match tcx.hir().find(hir_id) { + match tcx.hir().find_by_def_id(local_def_id) { Some(Node::Item(item)) => { return item.def_id; } _ => {} } let item = { + let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id); let mut parent_iter = tcx.hir().parent_iter(hir_id); loop { let node = parent_iter.next().map(|n| n.1); @@ -654,10 +652,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } // We want to nest trait/impl items in their parent, but nothing else. @@ -819,7 +817,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .iter() .filter_map(|param| match param.kind { GenericParamKind::Lifetime { .. } => { - Some(Region::early(&self.tcx.hir(), &mut index, param)) + Some(Region::early(self.tcx.hir(), &mut index, param)) } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { non_lifetime_count += 1; @@ -890,7 +888,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late(late_bound_idx as u32, &self.tcx.hir(), param); + let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param); let r = late_region_as_bound_region(self.tcx, &pair.1); (pair, r) }) @@ -1002,46 +1000,37 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // `fn foo<'a>() -> MyAnonTy<'a> { ... }` // ^ ^this gets resolved in the current scope for lifetime in lifetimes { - if let hir::GenericArg::Lifetime(lifetime) = lifetime { - self.visit_lifetime(lifetime); - - // Check for predicates like `impl for<'a> Trait>` - // and ban them. Type variables instantiated inside binders aren't - // well-supported at the moment, so this doesn't work. - // In the future, this should be fixed and this error should be removed. - let def = self.map.defs.get(&lifetime.hir_id).cloned(); - if let Some(Region::LateBound(_, _, def_id, _)) = def { - if let Some(def_id) = def_id.as_local() { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - // Ensure that the parent of the def is an item, not HRTB - let parent_id = self.tcx.hir().get_parent_node(hir_id); - // FIXME(cjgillot) Can this check be replaced by - // `let parent_is_item = parent_id.is_owner();`? - let parent_is_item = - if let Some(parent_def_id) = parent_id.as_owner() { - matches!( - self.tcx.hir().krate().owners.get(parent_def_id), - Some(Some(_)), - ) - } else { - false - }; - - if !parent_is_item { - if !self.trait_definition_only { - struct_span_err!( - self.tcx.sess, - lifetime.span, - E0657, - "`impl Trait` can only capture lifetimes \ - bound at the fn or impl level" - ) - .emit(); - } - self.uninsert_lifetime_on_error(lifetime, def.unwrap()); - } - } + let hir::GenericArg::Lifetime(lifetime) = lifetime else { + continue + }; + self.visit_lifetime(lifetime); + + // Check for predicates like `impl for<'a> Trait>` + // and ban them. Type variables instantiated inside binders aren't + // well-supported at the moment, so this doesn't work. + // In the future, this should be fixed and this error should be removed. + let def = self.map.defs.get(&lifetime.hir_id).cloned(); + let Some(Region::LateBound(_, _, def_id, _)) = def else { + continue + }; + let Some(def_id) = def_id.as_local() else { + continue + }; + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + // Ensure that the parent of the def is an item, not HRTB + let parent_id = self.tcx.hir().get_parent_node(hir_id); + if !parent_id.is_owner() { + if !self.trait_definition_only { + struct_span_err!( + self.tcx.sess, + lifetime.span, + E0657, + "`impl Trait` can only capture lifetimes \ + bound at the fn or impl level" + ) + .emit(); } + self.uninsert_lifetime_on_error(lifetime, def.unwrap()); } } @@ -1056,7 +1045,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { for param in generics.params { match param.kind { GenericParamKind::Lifetime { .. } => { - let (name, reg) = Region::early(&self.tcx.hir(), &mut index, ¶m); + let (name, reg) = Region::early(self.tcx.hir(), &mut index, ¶m); let Region::EarlyBound(_, def_id, _) = reg else { bug!(); }; @@ -1137,7 +1126,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.missing_named_lifetime_spots.push((&trait_item.generics).into()); let tcx = self.tcx; self.visit_early_late( - Some(tcx.hir().get_parent_did(trait_item.hir_id())), + Some(tcx.hir().get_parent_item(trait_item.hir_id())), trait_item.hir_id(), &sig.decl, &trait_item.generics, @@ -1156,7 +1145,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .iter() .filter_map(|param| match param.kind { GenericParamKind::Lifetime { .. } => { - Some(Region::early(&self.tcx.hir(), &mut index, param)) + Some(Region::early(self.tcx.hir(), &mut index, param)) } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { non_lifetime_count += 1; @@ -1206,7 +1195,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.missing_named_lifetime_spots.push((&impl_item.generics).into()); let tcx = self.tcx; self.visit_early_late( - Some(tcx.hir().get_parent_did(impl_item.hir_id())), + Some(tcx.hir().get_parent_item(impl_item.hir_id())), impl_item.hir_id(), &sig.decl, &impl_item.generics, @@ -1225,7 +1214,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .iter() .filter_map(|param| match param.kind { GenericParamKind::Lifetime { .. } => { - Some(Region::early(&self.tcx.hir(), &mut index, param)) + Some(Region::early(self.tcx.hir(), &mut index, param)) } GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => { non_lifetime_count += 1; @@ -1379,7 +1368,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .enumerate() .map(|(late_bound_idx, param)| { let pair = - Region::late(late_bound_idx as u32, &this.tcx.hir(), param); + Region::late(late_bound_idx as u32, this.tcx.hir(), param); let r = late_region_as_bound_region(this.tcx, &pair.1); (pair, r) }) @@ -1474,11 +1463,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late( - initial_bound_vars + late_bound_idx as u32, - &self.tcx.hir(), - param, - ); + let pair = + Region::late(initial_bound_vars + late_bound_idx as u32, self.tcx.hir(), param); let r = late_region_as_bound_region(self.tcx, &pair.1); lifetimes.insert(pair.0, pair.1); r @@ -1619,12 +1605,6 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { gather.visit_body(body); impl<'v, 'a, 'tcx> Visitor<'v> for GatherLabels<'a, 'tcx> { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_expr(&mut self, ex: &hir::Expr<'_>) { if let Some(label) = expression_label(ex) { for prior_label in &self.labels_in_fn[..] { @@ -1675,13 +1655,10 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { if let Some(def) = lifetimes.get(&hir::ParamName::Plain(label.normalize_to_macros_2_0())) { - let hir_id = - tcx.hir().local_def_id_to_hir_id(def.id().unwrap().expect_local()); - signal_shadowing_problem( tcx, label.name, - original_lifetime(tcx.hir().span(hir_id)), + original_lifetime(tcx.def_span(def.id().unwrap().expect_local())), shadower_label(label.span), ); return; @@ -1693,10 +1670,10 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { } } -fn compute_object_lifetime_defaults( - tcx: TyCtxt<'_>, +fn compute_object_lifetime_defaults<'tcx>( + tcx: TyCtxt<'tcx>, item: &hir::Item<'_>, -) -> Option> { +) -> Option<&'tcx [ObjectLifetimeDefault]> { match item.kind { hir::ItemKind::Struct(_, ref generics) | hir::ItemKind::Union(_, ref generics) @@ -1749,10 +1726,10 @@ fn compute_object_lifetime_defaults( /// Scan the bounds and where-clauses on parameters to extract bounds /// of the form `T:'a` so as to determine the `ObjectLifetimeDefault` /// for each type parameter. -fn object_lifetime_defaults_for_item( - tcx: TyCtxt<'_>, +fn object_lifetime_defaults_for_item<'tcx>( + tcx: TyCtxt<'tcx>, generics: &hir::Generics<'_>, -) -> Vec { +) -> &'tcx [ObjectLifetimeDefault] { fn add_bounds(set: &mut Set1, bounds: &[hir::GenericBound<'_>]) { for bound in bounds { if let hir::GenericBound::Outlives(ref lifetime) = *bound { @@ -1761,81 +1738,75 @@ fn object_lifetime_defaults_for_item( } } - generics - .params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => None, - GenericParamKind::Type { .. } => { - let mut set = Set1::Empty; - - add_bounds(&mut set, ¶m.bounds); - - let param_def_id = tcx.hir().local_def_id(param.hir_id); - for predicate in generics.where_clause.predicates { - // Look for `type: ...` where clauses. - let data = match *predicate { - hir::WherePredicate::BoundPredicate(ref data) => data, - _ => continue, - }; + let process_param = |param: &hir::GenericParam<'_>| match param.kind { + GenericParamKind::Lifetime { .. } => None, + GenericParamKind::Type { .. } => { + let mut set = Set1::Empty; - // Ignore `for<'a> type: ...` as they can change what - // lifetimes mean (although we could "just" handle it). - if !data.bound_generic_params.is_empty() { - continue; - } + add_bounds(&mut set, ¶m.bounds); - let res = match data.bounded_ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.res, - _ => continue, - }; + let param_def_id = tcx.hir().local_def_id(param.hir_id); + for predicate in generics.where_clause.predicates { + // Look for `type: ...` where clauses. + let data = match *predicate { + hir::WherePredicate::BoundPredicate(ref data) => data, + _ => continue, + }; - if res == Res::Def(DefKind::TyParam, param_def_id.to_def_id()) { - add_bounds(&mut set, &data.bounds); - } + // Ignore `for<'a> type: ...` as they can change what + // lifetimes mean (although we could "just" handle it). + if !data.bound_generic_params.is_empty() { + continue; } - Some(match set { - Set1::Empty => Set1::Empty, - Set1::One(name) => { - if name == hir::LifetimeName::Static { - Set1::One(Region::Static) - } else { - generics - .params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(( - param.hir_id, - hir::LifetimeName::Param(param.name), - LifetimeDefOrigin::from_param(param), - )), - _ => None, - }) - .enumerate() - .find(|&(_, (_, lt_name, _))| lt_name == name) - .map_or(Set1::Many, |(i, (id, _, origin))| { - let def_id = tcx.hir().local_def_id(id); - Set1::One(Region::EarlyBound( - i as u32, - def_id.to_def_id(), - origin, - )) - }) - } - } - Set1::Many => Set1::Many, - }) - } - GenericParamKind::Const { .. } => { - // Generic consts don't impose any constraints. - // - // We still store a dummy value here to allow generic parameters - // in an arbitrary order. - Some(Set1::Empty) + let res = match data.bounded_ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.res, + _ => continue, + }; + + if res == Res::Def(DefKind::TyParam, param_def_id.to_def_id()) { + add_bounds(&mut set, &data.bounds); + } } - }) - .collect() + + Some(match set { + Set1::Empty => Set1::Empty, + Set1::One(name) => { + if name == hir::LifetimeName::Static { + Set1::One(Region::Static) + } else { + generics + .params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => Some(( + param.hir_id, + hir::LifetimeName::Param(param.name), + LifetimeDefOrigin::from_param(param), + )), + _ => None, + }) + .enumerate() + .find(|&(_, (_, lt_name, _))| lt_name == name) + .map_or(Set1::Many, |(i, (id, _, origin))| { + let def_id = tcx.hir().local_def_id(id); + Set1::One(Region::EarlyBound(i as u32, def_id.to_def_id(), origin)) + }) + } + } + Set1::Many => Set1::Many, + }) + } + GenericParamKind::Const { .. } => { + // Generic consts don't impose any constraints. + // + // We still store a dummy value here to allow generic parameters + // in an arbitrary order. + Some(Set1::Empty) + } + }; + + tcx.arena.alloc_from_iter(generics.params.iter().filter_map(process_param)) } impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { @@ -1913,6 +1884,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let remove_decl = self .tcx .parent(def_id) + .and_then(|parent_def_id| parent_def_id.as_local()) .and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id)) .and_then(|generics| self.lifetime_deletion_span(name, generics)); @@ -1953,7 +1925,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }; if let Node::Lifetime(hir_lifetime) = self.tcx.hir().get(lifetime.hir_id) { if let Some(parent) = - self.tcx.hir().find(self.tcx.hir().get_parent_item(hir_lifetime.hir_id)) + self.tcx.hir().find_by_def_id(self.tcx.hir().get_parent_item(hir_lifetime.hir_id)) { match parent { Node::Item(item) => { @@ -2035,19 +2007,20 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { match lifetimeuseset { Some(LifetimeUseSet::One(lifetime)) => { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - debug!("hir id first={:?}", hir_id); - if let Some((id, span, name)) = match self.tcx.hir().get(hir_id) { - Node::Lifetime(hir_lifetime) => Some(( - hir_lifetime.hir_id, - hir_lifetime.span, - hir_lifetime.name.ident(), - )), - Node::GenericParam(param) => { - Some((param.hir_id, param.span, param.name.ident())) + debug!(?def_id); + if let Some((id, span, name)) = + match self.tcx.hir().get_by_def_id(def_id.expect_local()) { + Node::Lifetime(hir_lifetime) => Some(( + hir_lifetime.hir_id, + hir_lifetime.span, + hir_lifetime.name.ident(), + )), + Node::GenericParam(param) => { + Some((param.hir_id, param.span, param.name.ident())) + } + _ => None, } - _ => None, - } { + { debug!("id = {:?} span = {:?} name = {:?}", id, span, name); if name.name == kw::UnderscoreLifetime { continue; @@ -2055,12 +2028,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if let Some(parent_def_id) = self.tcx.parent(def_id) { if let Some(def_id) = parent_def_id.as_local() { - let parent_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); // lifetimes in `derive` expansions don't count (Issue #53738) if self .tcx - .hir() - .attrs(parent_hir_id) + .get_attrs(def_id.to_def_id()) .iter() .any(|attr| attr.has_name(sym::automatically_derived)) { @@ -2072,7 +2043,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if let hir::Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(ref opaque), .. - }) = self.tcx.hir().get(parent_hir_id) + }) = self.tcx.hir().get_by_def_id(def_id) { if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) { continue 'lifetimes; @@ -2118,18 +2089,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { debug!("not one use lifetime"); } None => { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - if let Some((id, span, name)) = match self.tcx.hir().get(hir_id) { - Node::Lifetime(hir_lifetime) => Some(( - hir_lifetime.hir_id, - hir_lifetime.span, - hir_lifetime.name.ident(), - )), - Node::GenericParam(param) => { - Some((param.hir_id, param.span, param.name.ident())) + if let Some((id, span, name)) = + match self.tcx.hir().get_by_def_id(def_id.expect_local()) { + Node::Lifetime(hir_lifetime) => Some(( + hir_lifetime.hir_id, + hir_lifetime.span, + hir_lifetime.name.ident(), + )), + Node::GenericParam(param) => { + Some((param.hir_id, param.span, param.name.ident())) + } + _ => None, } - _ => None, - } { + { debug!("id ={:?} span = {:?} name = {:?}", id, span, name); self.tcx.struct_span_lint_hir( lint::builtin::UNUSED_LIFETIMES, @@ -2140,7 +2112,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { .build(&format!("lifetime parameter `{}` never used", name)); if let Some(parent_def_id) = self.tcx.parent(def_id) { if let Some(generics) = - self.tcx.hir().get_generics(parent_def_id) + self.tcx.hir().get_generics(parent_def_id.expect_local()) { let unused_lt_span = self.lifetime_deletion_span(name, generics); @@ -2219,9 +2191,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if self.map.late_bound.contains(¶m.hir_id) { let late_bound_idx = named_late_bound_vars; named_late_bound_vars += 1; - Some(Region::late(late_bound_idx, &self.tcx.hir(), param)) + Some(Region::late(late_bound_idx, self.tcx.hir(), param)) } else { - Some(Region::early(&self.tcx.hir(), &mut next_early_index, param)) + Some(Region::early(self.tcx.hir(), &mut next_early_index, param)) } } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { @@ -2241,7 +2213,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }) .enumerate() .map(|(late_bound_idx, param)| { - let pair = Region::late(late_bound_idx as u32, &self.tcx.hir(), param); + let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param); late_region_as_bound_region(self.tcx, &pair.1) }) .collect(); @@ -2528,7 +2500,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }; if let Some(def_id) = def_id.as_local() { let id = self.tcx.hir().local_def_id_to_hir_id(def_id); - self.tcx.object_lifetime_defaults(id).unwrap().iter().map(set_to_region).collect() + self.tcx + .object_lifetime_defaults(id.owner) + .unwrap() + .iter() + .map(set_to_region) + .collect() } else { let tcx = self.tcx; self.xcrate_object_lifetime_defaults @@ -2764,7 +2741,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(_, ref m), .. }) => { if let hir::ItemKind::Trait(.., ref trait_items) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_did(parent)).kind + self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind { assoc_item_kind = trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind); @@ -2777,7 +2754,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) => { if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_did(parent)).kind + self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind { impl_self = Some(self_ty); assoc_item_kind = @@ -2816,7 +2793,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // Look for `self: &'a Self` - also desugared from `&'a self`, // and if that matches, use it for elision and return early. fn is_self_ty(&self, res: Res) -> bool { - if let Res::SelfTy(..) = res { + if let Res::SelfTy { .. } = res { return true; } @@ -2840,12 +2817,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } impl<'a> Visitor<'a> for SelfVisitor<'a> { - type Map = intravisit::ErasedMap<'a>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_ty(&mut self, ty: &'a hir::Ty<'a>) { if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind { if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind @@ -2930,12 +2901,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { if let hir::TyKind::BareFn(_) = ty.kind { self.outer_index.shift_in(1); @@ -3013,12 +2978,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { anon_count: u32, } impl<'v> Visitor<'v> for GatherAnonLifetimes { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - #[instrument(skip(self), level = "trace")] fn visit_ty(&mut self, ty: &hir::Ty<'_>) { // If we enter a `BareFn`, then we enter a *new* binding scope @@ -3342,13 +3301,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Binder { ref lifetimes, s, .. } => { if let Some(&def) = lifetimes.get(¶m.name.normalize_to_macros_2_0()) { - let hir_id = - self.tcx.hir().local_def_id_to_hir_id(def.id().unwrap().expect_local()); - signal_shadowing_problem( self.tcx, param.name.ident().name, - original_lifetime(self.tcx.hir().span(hir_id)), + original_lifetime(self.tcx.def_span(def.id().unwrap())), shadower_lifetime(¶m), ); return; @@ -3519,12 +3475,6 @@ fn insert_late_bound_lifetimes( } impl<'v> Visitor<'v> for ConstrainedCollector { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { match ty.kind { hir::TyKind::Path( @@ -3563,12 +3513,6 @@ fn insert_late_bound_lifetimes( } impl<'v> Visitor<'v> for AllCollector { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) { self.regions.insert(lifetime_ref.name.normalize_to_macros_2_0()); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b46a93c067..28d8d9247a 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -18,6 +18,7 @@ #![feature(nll)] #![recursion_limit = "256"] #![allow(rustdoc::private_intra_doc_links)] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; @@ -37,7 +38,7 @@ use rustc_ast::{ItemKind, ModKind, Path}; use rustc_ast_lowering::ResolverAstLowering; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_data_structures::ptr_key::PtrKey; +use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind}; @@ -49,10 +50,11 @@ use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; use rustc_hir::TraitCandidate; use rustc_index::vec::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; -use rustc_middle::hir::exports::ExportMap; +use rustc_middle::metadata::ModChild; +use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, DefIdTree, MainDefinition, ResolverOutputs}; +use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools, ResolverOutputs}; use rustc_query_system::ich::StableHashingContext; use rustc_session::cstore::{CrateStore, MetadataLoaderDyn}; use rustc_session::lint; @@ -66,7 +68,7 @@ use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::cell::{Cell, RefCell}; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeSet; use std::ops::ControlFlow; use std::{cmp, fmt, iter, mem, ptr}; use tracing::debug; @@ -77,8 +79,11 @@ use imports::{Import, ImportKind, ImportResolver, NameResolution}; use late::{ConstantItemKind, HasGenericParams, PathSource, Rib, RibKind::*}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; +use crate::access_levels::AccessLevelsVisitor; + type Res = def::Res; +mod access_levels; mod build_reduced_graph; mod check_unused; mod def_collector; @@ -258,6 +263,14 @@ enum ResolutionError<'a> { SelfInGenericParamDefault, /// Error E0767: use of unreachable label UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option }, + /// Error E0323, E0324, E0325: mismatch between trait item and impl item. + TraitImplMismatch { + name: Symbol, + kind: &'static str, + trait_path: String, + trait_item_span: Span, + code: rustc_errors::DiagnosticId, + }, } enum VisResolutionError<'a> { @@ -602,7 +615,8 @@ impl<'a> ModuleData<'a> { } } - fn def_id(&self) -> DefId { + // Public for rustdoc. + pub fn def_id(&self) -> DefId { self.opt_def_id().expect("`ModuleData::def_id` is called on a block module") } @@ -927,7 +941,7 @@ pub struct Resolver<'a> { /// `CrateNum` resolutions of `extern crate` items. extern_crate_map: FxHashMap, - export_map: ExportMap, + reexport_map: FxHashMap>, trait_map: NodeMap>, /// A map from nodes to anonymous modules. @@ -950,7 +964,7 @@ pub struct Resolver<'a> { /// language items. empty_module: Module<'a>, module_map: FxHashMap>, - binding_parent_modules: FxHashMap>, Module<'a>>, + binding_parent_modules: FxHashMap>, Module<'a>>, underscore_disambiguator: u32, /// Maps glob imports to the names of items actually imported. @@ -977,7 +991,7 @@ pub struct Resolver<'a> { macro_names: FxHashSet, builtin_macros: FxHashMap, registered_attrs: FxHashSet, - registered_tools: FxHashSet, + registered_tools: RegisteredTools, macro_use_prelude: FxHashMap>, all_macros: FxHashMap, macro_map: FxHashMap>, @@ -1047,11 +1061,13 @@ pub struct Resolver<'a> { item_generics_num_lifetimes: FxHashMap, main_def: Option, - trait_impls: BTreeMap>, + trait_impls: FxIndexMap>, /// A list of proc macro LocalDefIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. proc_macros: Vec, confused_type_with_std_module: FxHashMap, + + access_levels: AccessLevels, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1099,7 +1115,7 @@ impl<'a> ResolverArenas<'a> { self.name_resolutions.alloc(Default::default()) } fn alloc_macro_rules_scope(&'a self, scope: MacroRulesScope<'a>) -> MacroRulesScopeRef<'a> { - PtrKey(self.dropless.alloc(Cell::new(scope))) + Interned::new_unchecked(self.dropless.alloc(Cell::new(scope))) } fn alloc_macro_rules_binding( &'a self, @@ -1333,7 +1349,7 @@ impl<'a> Resolver<'a> { import_res_map: Default::default(), label_res_map: Default::default(), extern_crate_map: Default::default(), - export_map: FxHashMap::default(), + reexport_map: FxHashMap::default(), trait_map: NodeMap::default(), underscore_disambiguator: 0, empty_module, @@ -1407,6 +1423,7 @@ impl<'a> Resolver<'a> { trait_impls: Default::default(), proc_macros: Default::default(), confused_type_with_std_module: Default::default(), + access_levels: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); @@ -1446,18 +1463,20 @@ impl<'a> Resolver<'a> { let definitions = self.definitions; let visibilities = self.visibilities; let extern_crate_map = self.extern_crate_map; - let export_map = self.export_map; + let reexport_map = self.reexport_map; let maybe_unused_trait_imports = self.maybe_unused_trait_imports; let maybe_unused_extern_crates = self.maybe_unused_extern_crates; let glob_map = self.glob_map; let main_def = self.main_def; let confused_type_with_std_module = self.confused_type_with_std_module; + let access_levels = self.access_levels; ResolverOutputs { definitions, cstore: Box::new(self.crate_loader.into_cstore()), visibilities, + access_levels, extern_crate_map, - export_map, + reexport_map, glob_map, maybe_unused_trait_imports, maybe_unused_extern_crates, @@ -1470,6 +1489,7 @@ impl<'a> Resolver<'a> { trait_impls: self.trait_impls, proc_macros, confused_type_with_std_module, + registered_tools: self.registered_tools, } } @@ -1477,10 +1497,11 @@ impl<'a> Resolver<'a> { let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect(); ResolverOutputs { definitions: self.definitions.clone(), + access_levels: self.access_levels.clone(), cstore: Box::new(self.cstore().clone()), visibilities: self.visibilities.clone(), extern_crate_map: self.extern_crate_map.clone(), - export_map: self.export_map.clone(), + reexport_map: self.reexport_map.clone(), glob_map: self.glob_map.clone(), maybe_unused_trait_imports: self.maybe_unused_trait_imports.clone(), maybe_unused_extern_crates: self.maybe_unused_extern_crates.clone(), @@ -1493,6 +1514,7 @@ impl<'a> Resolver<'a> { trait_impls: self.trait_impls.clone(), proc_macros, confused_type_with_std_module: self.confused_type_with_std_module.clone(), + registered_tools: self.registered_tools.clone(), } } @@ -1532,6 +1554,9 @@ impl<'a> Resolver<'a> { pub fn resolve_crate(&mut self, krate: &Crate) { self.session.time("resolve_crate", || { self.session.time("finalize_imports", || ImportResolver { r: self }.finalize_imports()); + self.session.time("resolve_access_levels", || { + AccessLevelsVisitor::compute_access_levels(self, krate) + }); 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_main", || self.resolve_main()); @@ -2759,7 +2784,7 @@ impl<'a> Resolver<'a> { return Res::Err; } } - Res::Def(DefKind::TyParam, _) | Res::SelfTy(..) => { + Res::Def(DefKind::TyParam, _) | Res::SelfTy { .. } => { for rib in ribs { let has_generic_params: HasGenericParams = match rib.kind { NormalRibKind @@ -2779,8 +2804,8 @@ impl<'a> Resolver<'a> { // 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. - if let Res::SelfTy(trait_def, Some((impl_def, _))) = res { - res = Res::SelfTy(trait_def, Some((impl_def, true))); + if let Res::SelfTy { trait_, alias_to: Some((def, _)) } = res { + res = Res::SelfTy { trait_, alias_to: Some((def, true)) } } else { if record_used { self.report_error( @@ -2913,7 +2938,9 @@ impl<'a> Resolver<'a> { } fn set_binding_parent_module(&mut self, binding: &'a NameBinding<'a>, module: Module<'a>) { - if let Some(old_module) = self.binding_parent_modules.insert(PtrKey(binding), module) { + if let Some(old_module) = + self.binding_parent_modules.insert(Interned::new_unchecked(binding), module) + { if !ptr::eq(module, old_module) { span_bug!(binding.span, "parent module is reset for binding"); } @@ -2929,8 +2956,8 @@ impl<'a> Resolver<'a> { // is disambiguated to mitigate regressions from macro modularization. // Scoping for `macro_rules` behaves like scoping for `let` at module level, in general. match ( - self.binding_parent_modules.get(&PtrKey(macro_rules)), - self.binding_parent_modules.get(&PtrKey(modularized)), + self.binding_parent_modules.get(&Interned::new_unchecked(macro_rules)), + self.binding_parent_modules.get(&Interned::new_unchecked(modularized)), ) { (Some(macro_rules), Some(modularized)) => { macro_rules.nearest_parent_mod() == modularized.nearest_parent_mod() @@ -3384,6 +3411,16 @@ impl<'a> Resolver<'a> { &self.all_macros } + /// For rustdoc. + /// For local modules returns only reexports, for external modules returns all children. + pub fn module_children_or_reexports(&self, def_id: DefId) -> Vec { + if let Some(def_id) = def_id.as_local() { + self.reexport_map.get(&def_id).cloned().unwrap_or_default() + } else { + self.cstore().module_children_untracked(def_id, self.session) + } + } + /// Retrieves the span of the given `DefId` if `DefId` is in the local crate. #[inline] pub fn opt_span(&self, def_id: DefId) -> Option { @@ -3421,7 +3458,6 @@ impl<'a> Resolver<'a> { let attr = self .cstore() .item_attrs_untracked(def_id, self.session) - .into_iter() .find(|a| a.has_name(sym::rustc_legacy_const_generics))?; let mut ret = Vec::new(); for meta in attr.meta_item_list()? { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 52685ec697..89c2a0c74b 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -11,7 +11,7 @@ use rustc_ast_lowering::ResolverAstLowering; use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::ptr_key::PtrKey; +use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_errors::struct_span_err; use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand}; @@ -23,7 +23,7 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, LocalDefId}; use rustc_hir::PrimTy; use rustc_middle::middle::stability; -use rustc_middle::ty; +use rustc_middle::ty::{self, RegisteredTools}; use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK}; use rustc_session::lint::builtin::{SOFT_UNSTABLE, UNUSED_MACROS}; use rustc_session::lint::BuiltinLintDiagnostics; @@ -71,7 +71,7 @@ pub enum MacroRulesScope<'a> { /// This helps to avoid uncontrollable growth of `macro_rules!` scope chains, /// which usually grow lineraly with the number of macro invocations /// in a module (including derives) and hurt performance. -pub(crate) type MacroRulesScopeRef<'a> = PtrKey<'a, Cell>>; +pub(crate) type MacroRulesScopeRef<'a> = Interned<'a, Cell>>; // Macro namespace is separated into two sub-namespaces, one for bang macros and // one for attribute-like macros (attributes, derives). @@ -447,6 +447,10 @@ impl<'a> ResolverExpand for Resolver<'a> { fn declare_proc_macro(&mut self, id: NodeId) { self.proc_macros.push(id) } + + fn registered_tools(&self) -> &RegisteredTools { + &self.registered_tools + } } impl<'a> Resolver<'a> { diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 23f5b17fa7..0ff56a30ea 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -21,7 +21,7 @@ use rustc_hir::def::{DefKind as HirDefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir_pretty::{bounds_to_string, fn_to_string, generic_params_to_string, ty_to_string}; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::span_bug; use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::config::Input; @@ -47,11 +47,10 @@ use rls_data::{ use tracing::{debug, error}; +#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5213 macro_rules! down_cast_data { ($id:ident, $kind:ident, $sp:expr) => { - let $id = if let super::Data::$kind(data) = $id { - data - } else { + let super::Data::$kind($id) = $id else { span_bug!($sp, "unexpected data kind: {:?}", $id); }; }; @@ -263,7 +262,7 @@ impl<'tcx> DumpVisitor<'tcx> { ) { debug!("process_method: {:?}:{}", def_id, ident); - let map = &self.tcx.hir(); + let map = self.tcx.hir(); let hir_id = map.local_def_id_to_hir_id(def_id); self.nest_typeck_results(def_id, |v| { if let Some(mut method_data) = v.save_ctxt.get_method_data(hir_id, ident, span) { @@ -362,7 +361,7 @@ impl<'tcx> DumpVisitor<'tcx> { ty_params: &'tcx hir::Generics<'tcx>, body: hir::BodyId, ) { - let map = &self.tcx.hir(); + let map = self.tcx.hir(); self.nest_typeck_results(item.def_id, |v| { let body = map.body(body); if let Some(fn_data) = v.save_ctxt.get_item_data(item) { @@ -627,7 +626,7 @@ impl<'tcx> DumpVisitor<'tcx> { } } - let map = &self.tcx.hir(); + let map = self.tcx.hir(); self.nest_typeck_results(item.def_id, |v| { v.visit_ty(&impl_.self_ty); if let Some(trait_ref) = &impl_.of_trait { @@ -717,7 +716,7 @@ impl<'tcx> DumpVisitor<'tcx> { // walk generics and methods self.process_generic_params(generics, &qualname, item.hir_id()); for method in methods { - let map = &self.tcx.hir(); + let map = self.tcx.hir(); self.process_trait_item(map.trait_item(method.id), item.def_id.to_def_id()) } } @@ -922,7 +921,7 @@ impl<'tcx> DumpVisitor<'tcx> { | HirDefKind::AssocTy, _, ) - | Res::SelfTy(..) => { + | Res::SelfTy { .. } => { self.dump_path_segment_ref(id, &hir::PathSegment::from_ident(ident)); } def => { @@ -1137,10 +1136,10 @@ impl<'tcx> DumpVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { @@ -1363,9 +1362,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { let res = self.save_ctxt.get_path_res(hir_expr.hir_id); self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *rest) } - hir::ExprKind::MethodCall(ref seg, _, args, _) => { - self.process_method_call(ex, seg, args) - } + hir::ExprKind::MethodCall(ref seg, args, _) => self.process_method_call(ex, seg, args), hir::ExprKind::Field(ref sub_ex, _) => { self.visit_expr(&sub_ex); diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 7ec619e07f..8b0adba9fa 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -1,7 +1,9 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(if_let_guard)] #![feature(nll)] +#![feature(let_else)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] mod dump_visitor; mod dumper; @@ -18,7 +20,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Node; use rustc_hir_pretty::{enum_def_to_string, fn_to_string, ty_to_string}; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::ty::{self, print::with_no_trimmed_paths, DefIdTree, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -710,13 +712,11 @@ impl<'tcx> SaveContext<'tcx> { } Res::Def(HirDefKind::AssocFn, decl_id) => { let def_id = if decl_id.is_local() { - let ti = self.tcx.associated_item(decl_id); - - self.tcx - .associated_items(ti.container.id()) - .filter_by_name_unhygienic(ti.ident.name) - .find(|item| item.defaultness.has_value()) - .map(|item| item.def_id) + if self.tcx.associated_item(decl_id).defaultness.has_value() { + Some(decl_id) + } else { + None + } } else { None }; @@ -749,7 +749,7 @@ impl<'tcx> SaveContext<'tcx> { _, ) | Res::PrimTy(..) - | Res::SelfTy(..) + | Res::SelfTy { .. } | Res::ToolMod | Res::NonMacroAttr(..) | Res::SelfCtor(..) @@ -814,7 +814,7 @@ impl<'tcx> SaveContext<'tcx> { fn lookup_def_id(&self, ref_id: hir::HirId) -> Option { match self.get_path_res(ref_id) { - Res::PrimTy(_) | Res::SelfTy(..) | Res::Err => None, + Res::PrimTy(_) | Res::SelfTy { .. } | Res::Err => None, def => def.opt_def_id(), } } @@ -823,9 +823,9 @@ impl<'tcx> SaveContext<'tcx> { let mut result = String::new(); for attr in attrs { - if let Some(val) = attr.doc_str() { + if let Some((val, kind)) = attr.doc_str_and_comment_kind() { // FIXME: Should save-analysis beautify doc strings itself or leave it to users? - result.push_str(beautify_doc_string(val).as_str()); + result.push_str(beautify_doc_string(val, kind).as_str()); result.push('\n'); } } @@ -861,10 +861,10 @@ impl<'l> PathCollector<'l> { } impl<'l> Visitor<'l> for PathCollector<'l> { - type Map = Map<'l>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_pat(&mut self, p: &'l hir::Pat<'l>) { @@ -986,7 +986,7 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>( tcx.dep_graph.with_ignore(|| { info!("Dumping crate {}", cratename); - // Privacy checking requires and is done after type checking; use a + // Privacy checking must be done outside of type inference; use a // fallback in case the access levels couldn't have been correctly computed. let access_levels = match tcx.sess.compile_status() { Ok(..) => tcx.privacy_access_levels(()), diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 4971bb6d1a..3bb1d2ff35 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -573,7 +573,7 @@ impl<'hir> Sig for hir::Path<'hir> { let res = scx.get_path_res(id.ok_or("Missing id for Path")?); let (name, start, end) = match res { - Res::PrimTy(..) | Res::SelfTy(..) | Res::Err => { + Res::PrimTy(..) | Res::SelfTy { .. } | Res::Err => { return Ok(Signature { text: path_to_string(self), defs: vec![], refs: vec![] }); } Res::Def(DefKind::AssocConst | DefKind::Variant | DefKind::Ctor(..), _) => { diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml index 49778f8225..f6b9e17e58 100644 --- a/compiler/rustc_serialize/Cargo.toml +++ b/compiler/rustc_serialize/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] -indexmap = "1" +indexmap = "1.8.0" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } [dev-dependencies] diff --git a/compiler/rustc_serialize/src/collection_impls.rs b/compiler/rustc_serialize/src/collection_impls.rs index 80a7f65018..02b28f7c62 100644 --- a/compiler/rustc_serialize/src/collection_impls.rs +++ b/compiler/rustc_serialize/src/collection_impls.rs @@ -17,15 +17,8 @@ impl>> Encodable for SmallVec { } impl>> Decodable for SmallVec { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut vec = SmallVec::with_capacity(len); - // FIXME(#48994) - could just be collected into a Result - for _ in 0..len { - vec.push(d.read_seq_elt(|d| Decodable::decode(d))?); - } - Ok(vec) - }) + fn decode(d: &mut D) -> SmallVec { + d.read_seq(|d, len| (0..len).map(|_| d.read_seq_elt(|d| Decodable::decode(d))).collect()) } } @@ -41,14 +34,8 @@ impl> Encodable for LinkedList { } impl> Decodable for LinkedList { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut list = LinkedList::new(); - for _ in 0..len { - list.push_back(d.read_seq_elt(|d| Decodable::decode(d))?); - } - Ok(list) - }) + fn decode(d: &mut D) -> LinkedList { + d.read_seq(|d, len| (0..len).map(|_| d.read_seq_elt(|d| Decodable::decode(d))).collect()) } } @@ -64,14 +51,8 @@ impl> Encodable for VecDeque { } impl> Decodable for VecDeque { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut deque: VecDeque = VecDeque::with_capacity(len); - for _ in 0..len { - deque.push_back(d.read_seq_elt(|d| Decodable::decode(d))?); - } - Ok(deque) - }) + fn decode(d: &mut D) -> VecDeque { + d.read_seq(|d, len| (0..len).map(|_| d.read_seq_elt(|d| Decodable::decode(d))).collect()) } } @@ -96,15 +77,15 @@ where K: Decodable + PartialEq + Ord, V: Decodable, { - fn decode(d: &mut D) -> Result, D::Error> { + fn decode(d: &mut D) -> BTreeMap { d.read_map(|d, len| { let mut map = BTreeMap::new(); for _ in 0..len { - let key = d.read_map_elt_key(|d| Decodable::decode(d))?; - let val = d.read_map_elt_val(|d| Decodable::decode(d))?; + let key = d.read_map_elt_key(|d| Decodable::decode(d)); + let val = d.read_map_elt_val(|d| Decodable::decode(d)); map.insert(key, val); } - Ok(map) + map }) } } @@ -127,13 +108,13 @@ impl Decodable for BTreeSet where T: Decodable + PartialEq + Ord, { - fn decode(d: &mut D) -> Result, D::Error> { + fn decode(d: &mut D) -> BTreeSet { d.read_seq(|d, len| { let mut set = BTreeSet::new(); for _ in 0..len { - set.insert(d.read_seq_elt(|d| Decodable::decode(d))?); + set.insert(d.read_seq_elt(|d| Decodable::decode(d))); } - Ok(set) + set }) } } @@ -161,16 +142,16 @@ where V: Decodable, S: BuildHasher + Default, { - fn decode(d: &mut D) -> Result, D::Error> { + fn decode(d: &mut D) -> HashMap { d.read_map(|d, len| { let state = Default::default(); let mut map = HashMap::with_capacity_and_hasher(len, state); for _ in 0..len { - let key = d.read_map_elt_key(|d| Decodable::decode(d))?; - let val = d.read_map_elt_val(|d| Decodable::decode(d))?; + let key = d.read_map_elt_key(|d| Decodable::decode(d)); + let val = d.read_map_elt_val(|d| Decodable::decode(d)); map.insert(key, val); } - Ok(map) + map }) } } @@ -205,14 +186,14 @@ where T: Decodable + Hash + Eq, S: BuildHasher + Default, { - fn decode(d: &mut D) -> Result, D::Error> { + fn decode(d: &mut D) -> HashSet { d.read_seq(|d, len| { let state = Default::default(); let mut set = HashSet::with_capacity_and_hasher(len, state); for _ in 0..len { - set.insert(d.read_seq_elt(|d| Decodable::decode(d))?); + set.insert(d.read_seq_elt(|d| Decodable::decode(d))); } - Ok(set) + set }) } } @@ -240,16 +221,16 @@ where V: Decodable, S: BuildHasher + Default, { - fn decode(d: &mut D) -> Result, D::Error> { + fn decode(d: &mut D) -> indexmap::IndexMap { d.read_map(|d, len| { let state = Default::default(); let mut map = indexmap::IndexMap::with_capacity_and_hasher(len, state); for _ in 0..len { - let key = d.read_map_elt_key(|d| Decodable::decode(d))?; - let val = d.read_map_elt_val(|d| Decodable::decode(d))?; + let key = d.read_map_elt_key(|d| Decodable::decode(d)); + let val = d.read_map_elt_val(|d| Decodable::decode(d)); map.insert(key, val); } - Ok(map) + map }) } } @@ -274,14 +255,14 @@ where T: Decodable + Hash + Eq, S: BuildHasher + Default, { - fn decode(d: &mut D) -> Result, D::Error> { + fn decode(d: &mut D) -> indexmap::IndexSet { d.read_seq(|d, len| { let state = Default::default(); let mut set = indexmap::IndexSet::with_capacity_and_hasher(len, state); for _ in 0..len { - set.insert(d.read_seq_elt(|d| Decodable::decode(d))?); + set.insert(d.read_seq_elt(|d| Decodable::decode(d))); } - Ok(set) + set }) } } @@ -294,9 +275,9 @@ impl> Encodable for Rc<[T]> { } impl> Decodable for Rc<[T]> { - fn decode(d: &mut D) -> Result, D::Error> { - let vec: Vec = Decodable::decode(d)?; - Ok(vec.into()) + fn decode(d: &mut D) -> Rc<[T]> { + let vec: Vec = Decodable::decode(d); + vec.into() } } @@ -308,8 +289,8 @@ impl> Encodable for Arc<[T]> { } impl> Decodable for Arc<[T]> { - fn decode(d: &mut D) -> Result, D::Error> { - let vec: Vec = Decodable::decode(d)?; - Ok(vec.into()) + fn decode(d: &mut D) -> Arc<[T]> { + let vec: Vec = Decodable::decode(d); + vec.into() } } diff --git a/compiler/rustc_serialize/src/json.rs b/compiler/rustc_serialize/src/json.rs index cb9df3c338..6a39854924 100644 --- a/compiler/rustc_serialize/src/json.rs +++ b/compiler/rustc_serialize/src/json.rs @@ -89,7 +89,7 @@ //! let encoded = json::encode(&object).unwrap(); //! //! // Deserialize using `json::decode` -//! let decoded: TestStruct = json::decode(&encoded[..]).unwrap(); +//! let decoded: TestStruct = json::decode(&encoded[..]); //! ``` //! //! ## Using the `ToJson` trait @@ -173,7 +173,7 @@ //! let json_str: String = json_obj.to_string(); //! //! // Deserialize like before -//! let decoded: TestStruct = json::decode(&json_str).unwrap(); +//! let decoded: TestStruct = json::decode(&json_str); //! ``` use self::DecoderError::*; @@ -185,8 +185,6 @@ use self::ParserState::*; use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; -use std::io; -use std::io::prelude::*; use std::mem::swap; use std::num::FpCategory as Fp; use std::ops::Index; @@ -250,7 +248,6 @@ pub enum ErrorCode { pub enum ParserError { /// msg, line, col SyntaxError(ErrorCode, usize, usize), - IoError(io::ErrorKind, String), } // Builder and Parser have the same errors. @@ -265,6 +262,12 @@ pub enum DecoderError { ApplicationError(string::String), } +macro_rules! bad { + ($e:expr) => {{ + panic!("json decode error: {:?}", $e); + }}; +} + #[derive(Copy, Clone, Debug)] pub enum EncoderError { FmtError(fmt::Error), @@ -295,10 +298,10 @@ pub fn error_str(error: ErrorCode) -> &'static str { } /// Shortcut function to decode a JSON `&str` into an object -pub fn decode>(s: &str) -> DecodeResult { +pub fn decode>(s: &str) -> T { let json = match from_str(s) { Ok(x) => x, - Err(e) => return Err(ParseError(e)), + Err(e) => bad!(ParseError(e)), }; let mut decoder = Decoder::new(json); @@ -323,10 +326,6 @@ impl fmt::Display for ErrorCode { } } -fn io_error_to_error(io: io::Error) -> ParserError { - IoError(io.kind(), io.to_string()) -} - impl fmt::Display for ParserError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME this should be a nicer error @@ -334,15 +333,6 @@ impl fmt::Display for ParserError { } } -impl fmt::Display for DecoderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME this should be a nicer error - fmt::Debug::fmt(self, f) - } -} - -impl std::error::Error for DecoderError {} - impl fmt::Display for EncoderError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME this should be a nicer error @@ -2166,21 +2156,6 @@ impl> Builder { } } -/// Decodes a json value from an `&mut io::Read` -pub fn from_reader(rdr: &mut dyn Read) -> Result { - let mut contents = Vec::new(); - match rdr.read_to_end(&mut contents) { - Ok(c) => c, - Err(e) => return Err(io_error_to_error(e)), - }; - let s = match str::from_utf8(&contents).ok() { - Some(s) => s, - _ => return Err(SyntaxError(NotUtf8, 0, 0)), - }; - let mut builder = Builder::new(s.chars()); - builder.build() -} - /// Decodes a json value from a string pub fn from_str(s: &str) -> Result { let mut builder = Builder::new(s.chars()); @@ -2206,41 +2181,39 @@ impl Decoder { macro_rules! expect { ($e:expr, Null) => {{ match $e { - Json::Null => Ok(()), - other => Err(ExpectedError("Null".to_owned(), other.to_string())), + Json::Null => (), + other => bad!(ExpectedError("Null".to_owned(), other.to_string())), } }}; ($e:expr, $t:ident) => {{ match $e { - Json::$t(v) => Ok(v), - other => Err(ExpectedError(stringify!($t).to_owned(), other.to_string())), + Json::$t(v) => v, + other => bad!(ExpectedError(stringify!($t).to_owned(), other.to_string())), } }}; } macro_rules! read_primitive { ($name:ident, $ty:ty) => { - fn $name(&mut self) -> DecodeResult<$ty> { + fn $name(&mut self) -> $ty { match self.pop() { - Json::I64(f) => Ok(f as $ty), - Json::U64(f) => Ok(f as $ty), - Json::F64(f) => Err(ExpectedError("Integer".to_owned(), f.to_string())), + Json::I64(f) => f as $ty, + Json::U64(f) => f as $ty, + Json::F64(f) => bad!(ExpectedError("Integer".to_owned(), f.to_string())), // re: #12967.. a type w/ numeric keys (ie HashMap etc) // is going to have a string here, as per JSON spec. Json::String(s) => match s.parse().ok() { - Some(f) => Ok(f), - None => Err(ExpectedError("Number".to_owned(), s)), + Some(f) => f, + None => bad!(ExpectedError("Number".to_owned(), s)), }, - value => Err(ExpectedError("Number".to_owned(), value.to_string())), + value => bad!(ExpectedError("Number".to_owned(), value.to_string())), } } }; } impl crate::Decoder for Decoder { - type Error = DecoderError; - - fn read_nil(&mut self) -> DecodeResult<()> { + fn read_unit(&mut self) -> () { expect!(self.pop(), Null) } @@ -2257,156 +2230,150 @@ impl crate::Decoder for Decoder { read_primitive! { read_i64, i64 } read_primitive! { read_i128, i128 } - fn read_f32(&mut self) -> DecodeResult { - self.read_f64().map(|x| x as f32) + fn read_f32(&mut self) -> f32 { + self.read_f64() as f32 } - fn read_f64(&mut self) -> DecodeResult { + fn read_f64(&mut self) -> f64 { match self.pop() { - Json::I64(f) => Ok(f as f64), - Json::U64(f) => Ok(f as f64), - Json::F64(f) => Ok(f), + Json::I64(f) => f as f64, + Json::U64(f) => f as f64, + Json::F64(f) => f, Json::String(s) => { // re: #12967.. a type w/ numeric keys (ie HashMap etc) // is going to have a string here, as per JSON spec. match s.parse().ok() { - Some(f) => Ok(f), - None => Err(ExpectedError("Number".to_owned(), s)), + Some(f) => f, + None => bad!(ExpectedError("Number".to_owned(), s)), } } - Json::Null => Ok(f64::NAN), - value => Err(ExpectedError("Number".to_owned(), value.to_string())), + Json::Null => f64::NAN, + value => bad!(ExpectedError("Number".to_owned(), value.to_string())), } } - fn read_bool(&mut self) -> DecodeResult { + fn read_bool(&mut self) -> bool { expect!(self.pop(), Boolean) } - fn read_char(&mut self) -> DecodeResult { - let s = self.read_str()?; - { - let mut it = s.chars(); - if let (Some(c), None) = (it.next(), it.next()) { - // exactly one character - return Ok(c); - } + fn read_char(&mut self) -> char { + let s = self.read_str(); + let mut it = s.chars(); + if let (Some(c), None) = (it.next(), it.next()) { + // exactly one character + return c; } - Err(ExpectedError("single character string".to_owned(), s.to_string())) + bad!(ExpectedError("single character string".to_owned(), s.to_string())); } - fn read_str(&mut self) -> DecodeResult> { - expect!(self.pop(), String).map(Cow::Owned) + fn read_str(&mut self) -> Cow<'_, str> { + Cow::Owned(expect!(self.pop(), String)) } - fn read_raw_bytes_into(&mut self, s: &mut [u8]) -> Result<(), Self::Error> { + fn read_raw_bytes_into(&mut self, s: &mut [u8]) { for c in s.iter_mut() { - *c = self.read_u8()?; + *c = self.read_u8(); } - Ok(()) } - fn read_enum(&mut self, f: F) -> DecodeResult + fn read_enum(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { f(self) } - fn read_enum_variant(&mut self, names: &[&str], mut f: F) -> DecodeResult + fn read_enum_variant(&mut self, names: &[&str], mut f: F) -> T where - F: FnMut(&mut Decoder, usize) -> DecodeResult, + F: FnMut(&mut Decoder, usize) -> T, { let name = match self.pop() { Json::String(s) => s, Json::Object(mut o) => { let n = match o.remove("variant") { Some(Json::String(s)) => s, - Some(val) => return Err(ExpectedError("String".to_owned(), val.to_string())), - None => return Err(MissingFieldError("variant".to_owned())), + Some(val) => bad!(ExpectedError("String".to_owned(), val.to_string())), + None => bad!(MissingFieldError("variant".to_owned())), }; match o.remove("fields") { Some(Json::Array(l)) => { self.stack.extend(l.into_iter().rev()); } - Some(val) => return Err(ExpectedError("Array".to_owned(), val.to_string())), - None => return Err(MissingFieldError("fields".to_owned())), + Some(val) => bad!(ExpectedError("Array".to_owned(), val.to_string())), + None => bad!(MissingFieldError("fields".to_owned())), } n } - json => return Err(ExpectedError("String or Object".to_owned(), json.to_string())), + json => bad!(ExpectedError("String or Object".to_owned(), json.to_string())), }; let idx = match names.iter().position(|n| *n == &name[..]) { Some(idx) => idx, - None => return Err(UnknownVariantError(name)), + None => bad!(UnknownVariantError(name)), }; f(self, idx) } - fn read_enum_variant_arg(&mut self, f: F) -> DecodeResult + fn read_enum_variant_arg(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { f(self) } - fn read_struct(&mut self, f: F) -> DecodeResult + fn read_struct(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { - let value = f(self)?; + let value = f(self); self.pop(); - Ok(value) + value } - fn read_struct_field(&mut self, name: &str, f: F) -> DecodeResult + fn read_struct_field(&mut self, name: &str, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { - let mut obj = expect!(self.pop(), Object)?; + let mut obj = expect!(self.pop(), Object); let value = match obj.remove(name) { None => { // Add a Null and try to parse it as an Option<_> // to get None as a default value. self.stack.push(Json::Null); - match f(self) { - Ok(x) => x, - Err(_) => return Err(MissingFieldError(name.to_string())), - } + f(self) } Some(json) => { self.stack.push(json); - f(self)? + f(self) } }; self.stack.push(Json::Object(obj)); - Ok(value) + value } - fn read_tuple(&mut self, tuple_len: usize, f: F) -> DecodeResult + fn read_tuple(&mut self, tuple_len: usize, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { self.read_seq(move |d, len| { if len == tuple_len { f(d) } else { - Err(ExpectedError(format!("Tuple{}", tuple_len), format!("Tuple{}", len))) + bad!(ExpectedError(format!("Tuple{}", tuple_len), format!("Tuple{}", len))); } }) } - fn read_tuple_arg(&mut self, f: F) -> DecodeResult + fn read_tuple_arg(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { self.read_seq_elt(f) } - fn read_option(&mut self, mut f: F) -> DecodeResult + fn read_option(&mut self, mut f: F) -> T where - F: FnMut(&mut Decoder, bool) -> DecodeResult, + F: FnMut(&mut Decoder, bool) -> T, { match self.pop() { Json::Null => f(self, false), @@ -2417,28 +2384,28 @@ impl crate::Decoder for Decoder { } } - fn read_seq(&mut self, f: F) -> DecodeResult + fn read_seq(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder, usize) -> DecodeResult, + F: FnOnce(&mut Decoder, usize) -> T, { - let array = expect!(self.pop(), Array)?; + let array = expect!(self.pop(), Array); let len = array.len(); self.stack.extend(array.into_iter().rev()); f(self, len) } - fn read_seq_elt(&mut self, f: F) -> DecodeResult + fn read_seq_elt(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { f(self) } - fn read_map(&mut self, f: F) -> DecodeResult + fn read_map(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder, usize) -> DecodeResult, + F: FnOnce(&mut Decoder, usize) -> T, { - let obj = expect!(self.pop(), Object)?; + let obj = expect!(self.pop(), Object); let len = obj.len(); for (key, value) in obj { self.stack.push(value); @@ -2447,23 +2414,19 @@ impl crate::Decoder for Decoder { f(self, len) } - fn read_map_elt_key(&mut self, f: F) -> DecodeResult + fn read_map_elt_key(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { f(self) } - fn read_map_elt_val(&mut self, f: F) -> DecodeResult + fn read_map_elt_val(&mut self, f: F) -> T where - F: FnOnce(&mut Decoder) -> DecodeResult, + F: FnOnce(&mut Decoder) -> T, { f(self) } - - fn error(&mut self, err: &str) -> DecoderError { - ApplicationError(err.to_string()) - } } /// A trait for converting values to JSON diff --git a/compiler/rustc_serialize/src/leb128.rs b/compiler/rustc_serialize/src/leb128.rs index ea2df80e64..08b3c05420 100644 --- a/compiler/rustc_serialize/src/leb128.rs +++ b/compiler/rustc_serialize/src/leb128.rs @@ -53,16 +53,24 @@ impl_write_unsigned_leb128!(write_usize_leb128, usize); macro_rules! impl_read_unsigned_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; + pub fn $fn_name(slice: &[u8], position: &mut usize) -> $int_ty { + // The first iteration of this loop is unpeeled. This is a + // performance win because this code is hot and integer values less + // than 128 are very common, typically occurring 50-80% or more of + // the time, even for u64 and u128. + let byte = slice[*position]; + *position += 1; + if (byte & 0x80) == 0 { + return byte as $int_ty; + } + let mut result = (byte & 0x7F) as $int_ty; + let mut shift = 7; loop { - let byte = slice[position]; - position += 1; + let byte = slice[*position]; + *position += 1; if (byte & 0x80) == 0 { result |= (byte as $int_ty) << shift; - return (result, position); + return result; } else { result |= ((byte & 0x7F) as $int_ty) << shift; } @@ -122,15 +130,14 @@ impl_write_signed_leb128!(write_isize_leb128, isize); macro_rules! impl_read_signed_leb128 { ($fn_name:ident, $int_ty:ty) => { #[inline] - pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) { + pub fn $fn_name(slice: &[u8], position: &mut usize) -> $int_ty { let mut result = 0; let mut shift = 0; - let mut position = 0; let mut byte; loop { - byte = slice[position]; - position += 1; + byte = slice[*position]; + *position += 1; result |= <$int_ty>::from(byte & 0x7F) << shift; shift += 7; @@ -144,7 +151,7 @@ macro_rules! impl_read_signed_leb128 { result |= (!0 << shift); } - (result, position) + result } }; } diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index f2ef148168..7a05d2b762 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -130,8 +130,7 @@ impl serialize::Encoder for Encoder { #[inline] fn emit_i8(&mut self, v: i8) -> EncodeResult { - let as_u8: u8 = unsafe { std::mem::transmute(v) }; - self.emit_u8(as_u8) + self.emit_u8(v as u8) } #[inline] @@ -560,138 +559,126 @@ impl<'a> Decoder<'a> { } macro_rules! read_leb128 { - ($dec:expr, $fun:ident) => {{ - let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]); - $dec.position += bytes_read; - Ok(value) - }}; + ($dec:expr, $fun:ident) => {{ leb128::$fun($dec.data, &mut $dec.position) }}; } impl<'a> serialize::Decoder for Decoder<'a> { - type Error = String; - #[inline] - fn read_nil(&mut self) -> Result<(), Self::Error> { - Ok(()) + fn read_unit(&mut self) -> () { + () } #[inline] - fn read_u128(&mut self) -> Result { + fn read_u128(&mut self) -> u128 { read_leb128!(self, read_u128_leb128) } #[inline] - fn read_u64(&mut self) -> Result { + fn read_u64(&mut self) -> u64 { read_leb128!(self, read_u64_leb128) } #[inline] - fn read_u32(&mut self) -> Result { + fn read_u32(&mut self) -> u32 { read_leb128!(self, read_u32_leb128) } #[inline] - fn read_u16(&mut self) -> Result { + fn read_u16(&mut self) -> u16 { let bytes = [self.data[self.position], self.data[self.position + 1]]; let value = u16::from_le_bytes(bytes); self.position += 2; - Ok(value) + value } #[inline] - fn read_u8(&mut self) -> Result { + fn read_u8(&mut self) -> u8 { let value = self.data[self.position]; self.position += 1; - Ok(value) + value } #[inline] - fn read_usize(&mut self) -> Result { + fn read_usize(&mut self) -> usize { read_leb128!(self, read_usize_leb128) } #[inline] - fn read_i128(&mut self) -> Result { + fn read_i128(&mut self) -> i128 { read_leb128!(self, read_i128_leb128) } #[inline] - fn read_i64(&mut self) -> Result { + fn read_i64(&mut self) -> i64 { read_leb128!(self, read_i64_leb128) } #[inline] - fn read_i32(&mut self) -> Result { + fn read_i32(&mut self) -> i32 { read_leb128!(self, read_i32_leb128) } #[inline] - fn read_i16(&mut self) -> Result { + fn read_i16(&mut self) -> i16 { let bytes = [self.data[self.position], self.data[self.position + 1]]; let value = i16::from_le_bytes(bytes); self.position += 2; - Ok(value) + value } #[inline] - fn read_i8(&mut self) -> Result { - let as_u8 = self.data[self.position]; + fn read_i8(&mut self) -> i8 { + let value = self.data[self.position]; self.position += 1; - unsafe { Ok(::std::mem::transmute(as_u8)) } + value as i8 } #[inline] - fn read_isize(&mut self) -> Result { + fn read_isize(&mut self) -> isize { read_leb128!(self, read_isize_leb128) } #[inline] - fn read_bool(&mut self) -> Result { - let value = self.read_u8()?; - Ok(value != 0) + fn read_bool(&mut self) -> bool { + let value = self.read_u8(); + value != 0 } #[inline] - fn read_f64(&mut self) -> Result { - let bits = self.read_u64()?; - Ok(f64::from_bits(bits)) + fn read_f64(&mut self) -> f64 { + let bits = self.read_u64(); + f64::from_bits(bits) } #[inline] - fn read_f32(&mut self) -> Result { - let bits = self.read_u32()?; - Ok(f32::from_bits(bits)) + fn read_f32(&mut self) -> f32 { + let bits = self.read_u32(); + f32::from_bits(bits) } #[inline] - fn read_char(&mut self) -> Result { - let bits = self.read_u32()?; - Ok(std::char::from_u32(bits).unwrap()) + fn read_char(&mut self) -> char { + let bits = self.read_u32(); + std::char::from_u32(bits).unwrap() } #[inline] - fn read_str(&mut self) -> Result, Self::Error> { - let len = self.read_usize()?; + fn read_str(&mut self) -> Cow<'_, str> { + let len = self.read_usize(); let sentinel = self.data[self.position + len]; assert!(sentinel == STR_SENTINEL); let s = unsafe { std::str::from_utf8_unchecked(&self.data[self.position..self.position + len]) }; self.position += len + 1; - Ok(Cow::Borrowed(s)) + Cow::Borrowed(s) } #[inline] - fn error(&mut self, err: &str) -> Self::Error { - err.to_string() - } - - #[inline] - fn read_raw_bytes_into(&mut self, s: &mut [u8]) -> Result<(), String> { + fn read_raw_bytes_into(&mut self, s: &mut [u8]) { let start = self.position; self.position += s.len(); s.copy_from_slice(&self.data[start..self.position]); - Ok(()) } } @@ -719,9 +706,9 @@ impl serialize::Encodable for [u8] { // 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)?; - Ok(d.read_raw_bytes(len).to_owned()) + fn decode(d: &mut Decoder<'a>) -> Self { + let len = serialize::Decoder::read_usize(d); + d.read_raw_bytes(len).to_owned() } } @@ -756,13 +743,13 @@ impl serialize::Encodable for IntEncodedWithFixedSize { impl<'a> serialize::Decodable> for IntEncodedWithFixedSize { #[inline] - fn decode(decoder: &mut Decoder<'a>) -> Result { + fn decode(decoder: &mut Decoder<'a>) -> IntEncodedWithFixedSize { let _start_pos = decoder.position(); let bytes = decoder.read_raw_bytes(IntEncodedWithFixedSize::ENCODED_SIZE); let _end_pos = decoder.position(); debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); let value = u64::from_le_bytes(bytes.try_into().unwrap()); - Ok(IntEncodedWithFixedSize(value)) + IntEncodedWithFixedSize(value) } } diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 96a2231b59..a6172403fd 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -173,144 +173,145 @@ pub trait Encoder { } } +// Note: all the methods in this trait are infallible, which may be surprising. +// They used to be fallible (i.e. return a `Result`) but many of the impls just +// panicked when something went wrong, and for the cases that didn't the +// top-level invocation would also just panic on failure. Switching to +// infallibility made things faster and lots of code a little simpler and more +// concise. pub trait Decoder { - type Error; - // Primitive types: - fn read_nil(&mut self) -> Result<(), Self::Error>; - fn read_usize(&mut self) -> Result; - fn read_u128(&mut self) -> Result; - fn read_u64(&mut self) -> Result; - fn read_u32(&mut self) -> Result; - fn read_u16(&mut self) -> Result; - fn read_u8(&mut self) -> Result; - fn read_isize(&mut self) -> Result; - fn read_i128(&mut self) -> Result; - fn read_i64(&mut self) -> Result; - fn read_i32(&mut self) -> Result; - fn read_i16(&mut self) -> Result; - fn read_i8(&mut self) -> Result; - fn read_bool(&mut self) -> Result; - fn read_f64(&mut self) -> Result; - fn read_f32(&mut self) -> Result; - fn read_char(&mut self) -> Result; - fn read_str(&mut self) -> Result, Self::Error>; - fn read_raw_bytes_into(&mut self, s: &mut [u8]) -> Result<(), Self::Error>; + fn read_unit(&mut self) -> (); + fn read_usize(&mut self) -> usize; + fn read_u128(&mut self) -> u128; + fn read_u64(&mut self) -> u64; + fn read_u32(&mut self) -> u32; + fn read_u16(&mut self) -> u16; + fn read_u8(&mut self) -> u8; + fn read_isize(&mut self) -> isize; + fn read_i128(&mut self) -> i128; + fn read_i64(&mut self) -> i64; + fn read_i32(&mut self) -> i32; + fn read_i16(&mut self) -> i16; + fn read_i8(&mut self) -> i8; + fn read_bool(&mut self) -> bool; + fn read_f64(&mut self) -> f64; + fn read_f32(&mut self) -> f32; + fn read_char(&mut self) -> char; + fn read_str(&mut self) -> Cow<'_, str>; + fn read_raw_bytes_into(&mut self, s: &mut [u8]); // Compound types: #[inline] - fn read_enum(&mut self, f: F) -> Result + fn read_enum(&mut self, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } #[inline] - fn read_enum_variant(&mut self, _names: &[&str], mut f: F) -> Result + fn read_enum_variant(&mut self, _names: &[&str], mut f: F) -> T where - F: FnMut(&mut Self, usize) -> Result, + F: FnMut(&mut Self, usize) -> T, { - let disr = self.read_usize()?; + let disr = self.read_usize(); f(self, disr) } #[inline] - fn read_enum_variant_arg(&mut self, f: F) -> Result + fn read_enum_variant_arg(&mut self, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } #[inline] - fn read_struct(&mut self, f: F) -> Result + fn read_struct(&mut self, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } #[inline] - fn read_struct_field(&mut self, _f_name: &str, f: F) -> Result + fn read_struct_field(&mut self, _f_name: &str, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } #[inline] - fn read_tuple(&mut self, _len: usize, f: F) -> Result + fn read_tuple(&mut self, _len: usize, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } #[inline] - fn read_tuple_arg(&mut self, f: F) -> Result + fn read_tuple_arg(&mut self, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } // Specialized types: - fn read_option(&mut self, mut f: F) -> Result + fn read_option(&mut self, mut f: F) -> T where - F: FnMut(&mut Self, bool) -> Result, + F: FnMut(&mut Self, bool) -> T, { self.read_enum(move |this| { this.read_enum_variant(&["None", "Some"], move |this, idx| match idx { 0 => f(this, false), 1 => f(this, true), - _ => Err(this.error("read_option: expected 0 for None or 1 for Some")), + _ => panic!("read_option: expected 0 for None or 1 for Some"), }) }) } - fn read_seq(&mut self, f: F) -> Result + fn read_seq(&mut self, f: F) -> T where - F: FnOnce(&mut Self, usize) -> Result, + F: FnOnce(&mut Self, usize) -> T, { - let len = self.read_usize()?; + let len = self.read_usize(); f(self, len) } #[inline] - fn read_seq_elt(&mut self, f: F) -> Result + fn read_seq_elt(&mut self, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } - fn read_map(&mut self, f: F) -> Result + fn read_map(&mut self, f: F) -> T where - F: FnOnce(&mut Self, usize) -> Result, + F: FnOnce(&mut Self, usize) -> T, { - let len = self.read_usize()?; + let len = self.read_usize(); f(self, len) } #[inline] - fn read_map_elt_key(&mut self, f: F) -> Result + fn read_map_elt_key(&mut self, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } #[inline] - fn read_map_elt_val(&mut self, f: F) -> Result + fn read_map_elt_val(&mut self, f: F) -> T where - F: FnOnce(&mut Self) -> Result, + F: FnOnce(&mut Self) -> T, { f(self) } - - // Failure - fn error(&mut self, err: &str) -> Self::Error; } /// Trait for types that can be serialized @@ -340,7 +341,7 @@ pub trait Encodable { /// * `TyDecodable` should be used for types that are only serialized in crate /// metadata or the incremental cache. This is most types in `rustc_middle`. pub trait Decodable: Sized { - fn decode(d: &mut D) -> Result; + fn decode(d: &mut D) -> Self; } macro_rules! direct_serialize_impls { @@ -353,7 +354,7 @@ macro_rules! direct_serialize_impls { } impl Decodable for $ty { - fn decode(d: &mut D) -> Result<$ty, D::Error> { + fn decode(d: &mut D) -> $ty { d.$read_method() } } @@ -387,7 +388,7 @@ impl Encodable for ! { } impl Decodable for ! { - fn decode(_d: &mut D) -> Result { + fn decode(_d: &mut D) -> ! { unreachable!() } } @@ -399,8 +400,8 @@ impl Encodable for ::std::num::NonZeroU32 { } impl Decodable for ::std::num::NonZeroU32 { - fn decode(d: &mut D) -> Result { - d.read_u32().map(|d| ::std::num::NonZeroU32::new(d).unwrap()) + fn decode(d: &mut D) -> Self { + ::std::num::NonZeroU32::new(d.read_u32()).unwrap() } } @@ -423,8 +424,8 @@ impl Encodable for String { } impl Decodable for String { - fn decode(d: &mut D) -> Result { - Ok(d.read_str()?.into_owned()) + fn decode(d: &mut D) -> String { + d.read_str().into_owned() } } @@ -435,8 +436,8 @@ impl Encodable for () { } impl Decodable for () { - fn decode(d: &mut D) -> Result<(), D::Error> { - d.read_nil() + fn decode(d: &mut D) -> () { + d.read_unit() } } @@ -447,16 +448,16 @@ impl Encodable for PhantomData { } impl Decodable for PhantomData { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_nil()?; - Ok(PhantomData) + fn decode(d: &mut D) -> PhantomData { + d.read_unit(); + PhantomData } } impl> Decodable for Box<[T]> { - fn decode(d: &mut D) -> Result, D::Error> { - let v: Vec = Decodable::decode(d)?; - Ok(v.into_boxed_slice()) + fn decode(d: &mut D) -> Box<[T]> { + let v: Vec = Decodable::decode(d); + v.into_boxed_slice() } } @@ -467,8 +468,8 @@ impl> Encodable for Rc { } impl> Decodable for Rc { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(Rc::new(Decodable::decode(d)?)) + fn decode(d: &mut D) -> Rc { + Rc::new(Decodable::decode(d)) } } @@ -491,13 +492,22 @@ impl> Encodable for Vec { } impl> Decodable for Vec { - default fn decode(d: &mut D) -> Result, D::Error> { + default fn decode(d: &mut D) -> Vec { d.read_seq(|d, len| { - let mut v = Vec::with_capacity(len); - for _ in 0..len { - v.push(d.read_seq_elt(|d| Decodable::decode(d))?); + // SAFETY: we set the capacity in advance, only write elements, and + // only set the length at the end once the writing has succeeded. + let mut vec = Vec::with_capacity(len); + unsafe { + let ptr: *mut T = vec.as_mut_ptr(); + for i in 0..len { + std::ptr::write( + ptr.offset(i as isize), + d.read_seq_elt(|d| Decodable::decode(d)), + ); + } + vec.set_len(len); } - Ok(v) + vec }) } } @@ -510,14 +520,14 @@ impl, const N: usize> Encodable for [T; N] { } impl Decodable for [u8; N] { - fn decode(d: &mut D) -> Result<[u8; N], D::Error> { + fn decode(d: &mut D) -> [u8; N] { d.read_seq(|d, len| { assert!(len == N); let mut v = [0u8; N]; for i in 0..len { - v[i] = d.read_seq_elt(|d| Decodable::decode(d))?; + v[i] = d.read_seq_elt(|d| Decodable::decode(d)); } - Ok(v) + v }) } } @@ -536,9 +546,9 @@ impl + ToOwned> Decodable for Cow<'static, [T]> where [T]: ToOwned>, { - fn decode(d: &mut D) -> Result, D::Error> { - let v: Vec = Decodable::decode(d)?; - Ok(Cow::Owned(v)) + fn decode(d: &mut D) -> Cow<'static, [T]> { + let v: Vec = Decodable::decode(d); + Cow::Owned(v) } } @@ -552,8 +562,8 @@ impl> Encodable for Option { } impl> Decodable for Option { - fn decode(d: &mut D) -> Result, D::Error> { - d.read_option(|d, b| if b { Ok(Some(Decodable::decode(d)?)) } else { Ok(None) }) + fn decode(d: &mut D) -> Option { + d.read_option(|d, b| if b { Some(Decodable::decode(d)) } else { None }) } } @@ -571,17 +581,12 @@ impl, T2: Encodable> Encodable for Result, T2: Decodable> Decodable for Result { - fn decode(d: &mut D) -> Result, D::Error> { + fn decode(d: &mut D) -> Result { d.read_enum(|d| { d.read_enum_variant(&["Ok", "Err"], |d, disr| match disr { - 0 => Ok(Ok(d.read_enum_variant_arg(|d| T1::decode(d))?)), - 1 => Ok(Err(d.read_enum_variant_arg(|d| T2::decode(d))?)), - _ => { - panic!( - "Encountered invalid discriminant while \ - decoding `Result`." - ); - } + 0 => Ok(d.read_enum_variant_arg(|d| T1::decode(d))), + 1 => Err(d.read_enum_variant_arg(|d| T2::decode(d))), + _ => panic!("Encountered invalid discriminant while decoding `Result`."), }) }) } @@ -609,13 +614,13 @@ macro_rules! tuple { ( $($name:ident,)+ ) => ( impl),+> Decodable for ($($name,)+) { #[allow(non_snake_case)] - fn decode(d: &mut D) -> Result<($($name,)+), D::Error> { + fn decode(d: &mut D) -> ($($name,)+) { let len: usize = count!($($name)+); d.read_tuple(len, |d| { - let ret = ($(d.read_tuple_arg(|d| -> Result<$name, D::Error> { + let ret = ($(d.read_tuple_arg(|d| -> $name { Decodable::decode(d) - })?,)+); - Ok(ret) + }),)+); + ret }) } } @@ -651,9 +656,9 @@ impl Encodable for path::PathBuf { } impl Decodable for path::PathBuf { - fn decode(d: &mut D) -> Result { - let bytes: String = Decodable::decode(d)?; - Ok(path::PathBuf::from(bytes)) + fn decode(d: &mut D) -> path::PathBuf { + let bytes: String = Decodable::decode(d); + path::PathBuf::from(bytes) } } @@ -664,8 +669,8 @@ impl + Copy> Encodable for Cell { } impl + Copy> Decodable for Cell { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(Cell::new(Decodable::decode(d)?)) + fn decode(d: &mut D) -> Cell { + Cell::new(Decodable::decode(d)) } } @@ -681,8 +686,8 @@ impl> Encodable for RefCell { } impl> Decodable for RefCell { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(RefCell::new(Decodable::decode(d)?)) + fn decode(d: &mut D) -> RefCell { + RefCell::new(Decodable::decode(d)) } } @@ -693,8 +698,8 @@ impl> Encodable for Arc { } impl> Decodable for Arc { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(Arc::new(Decodable::decode(d)?)) + fn decode(d: &mut D) -> Arc { + Arc::new(Decodable::decode(d)) } } @@ -704,7 +709,7 @@ impl> Encodable for Box { } } impl> Decodable for Box { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(Box::new(Decodable::decode(d)?)) + fn decode(d: &mut D) -> Box { + Box::new(Decodable::decode(d)) } } diff --git a/compiler/rustc_serialize/tests/json.rs b/compiler/rustc_serialize/tests/json.rs index a759fa1bf1..ede912bdfb 100644 --- a/compiler/rustc_serialize/tests/json.rs +++ b/compiler/rustc_serialize/tests/json.rs @@ -1,14 +1,10 @@ #![allow(rustc::internal)] -use json::DecoderError::*; use json::ErrorCode::*; use json::Json::*; use json::JsonEvent::*; use json::ParserError::*; -use json::{ - from_str, DecodeResult, Decoder, DecoderError, Encoder, EncoderError, Json, JsonEvent, Parser, - StackElement, -}; +use json::{from_str, Decoder, Encoder, EncoderError, Json, JsonEvent, Parser, StackElement}; use rustc_macros::{Decodable, Encodable}; use rustc_serialize::json; use rustc_serialize::{Decodable, Encodable}; @@ -26,27 +22,27 @@ struct OptionData { #[test] fn test_decode_option_none() { let s = "{}"; - let obj: OptionData = json::decode(s).unwrap(); + let obj: OptionData = json::decode(s); assert_eq!(obj, OptionData { opt: None }); } #[test] fn test_decode_option_some() { let s = "{ \"opt\": 10 }"; - let obj: OptionData = json::decode(s).unwrap(); + let obj: OptionData = json::decode(s); assert_eq!(obj, OptionData { opt: Some(10) }); } #[test] -fn test_decode_option_malformed() { - check_err::( - "{ \"opt\": [] }", - ExpectedError("Number".to_string(), "[]".to_string()), - ); - check_err::( - "{ \"opt\": false }", - ExpectedError("Number".to_string(), "false".to_string()), - ); +#[should_panic(expected = r#"ExpectedError("Number", "[]")"#)] +fn test_decode_option_malformed1() { + check_err::(r#"{ "opt": [] }"#); +} + +#[test] +#[should_panic(expected = r#"ExpectedError("Number", "false")"#)] +fn test_decode_option_malformed2() { + check_err::(r#"{ "opt": false }"#); } #[derive(PartialEq, Encodable, Decodable, Debug)] @@ -329,13 +325,13 @@ fn test_read_identifiers() { #[test] fn test_decode_identifiers() { - let v: () = json::decode("null").unwrap(); + let v: () = json::decode("null"); assert_eq!(v, ()); - let v: bool = json::decode("true").unwrap(); + let v: bool = json::decode("true"); assert_eq!(v, true); - let v: bool = json::decode("false").unwrap(); + let v: bool = json::decode("false"); assert_eq!(v, false); } @@ -368,42 +364,42 @@ fn test_read_number() { } #[test] +#[should_panic(expected = r#"ExpectedError("Integer", "765.25")"#)] fn test_decode_numbers() { - let v: f64 = json::decode("3").unwrap(); + let v: f64 = json::decode("3"); assert_eq!(v, 3.0); - let v: f64 = json::decode("3.1").unwrap(); + let v: f64 = json::decode("3.1"); assert_eq!(v, 3.1); - let v: f64 = json::decode("-1.2").unwrap(); + let v: f64 = json::decode("-1.2"); assert_eq!(v, -1.2); - let v: f64 = json::decode("0.4").unwrap(); + let v: f64 = json::decode("0.4"); assert_eq!(v, 0.4); - let v: f64 = json::decode("0.4e5").unwrap(); + let v: f64 = json::decode("0.4e5"); assert_eq!(v, 0.4e5); - let v: f64 = json::decode("0.4e15").unwrap(); + let v: f64 = json::decode("0.4e15"); assert_eq!(v, 0.4e15); - let v: f64 = json::decode("0.4e-01").unwrap(); + let v: f64 = json::decode("0.4e-01"); assert_eq!(v, 0.4e-01); - let v: u64 = json::decode("0").unwrap(); + let v: u64 = json::decode("0"); assert_eq!(v, 0); - let v: u64 = json::decode("18446744073709551615").unwrap(); + let v: u64 = json::decode("18446744073709551615"); assert_eq!(v, u64::MAX); - let v: i64 = json::decode("-9223372036854775808").unwrap(); + let v: i64 = json::decode("-9223372036854775808"); assert_eq!(v, i64::MIN); - let v: i64 = json::decode("9223372036854775807").unwrap(); + let v: i64 = json::decode("9223372036854775807"); assert_eq!(v, i64::MAX); - let res: DecodeResult = json::decode("765.25"); - assert_eq!(res, Err(ExpectedError("Integer".to_string(), "765.25".to_string()))); + json::decode::("765.25"); } #[test] @@ -438,7 +434,7 @@ fn test_decode_str() { ]; for (i, o) in s { - let v: string::String = json::decode(i).unwrap(); + let v: string::String = json::decode(i); assert_eq!(v, o); } } @@ -463,39 +459,41 @@ fn test_read_array() { #[test] fn test_decode_array() { - let v: Vec<()> = json::decode("[]").unwrap(); + let v: Vec<()> = json::decode("[]"); assert_eq!(v, []); - let v: Vec<()> = json::decode("[null]").unwrap(); + let v: Vec<()> = json::decode("[null]"); assert_eq!(v, [()]); - let v: Vec = json::decode("[true]").unwrap(); + let v: Vec = json::decode("[true]"); assert_eq!(v, [true]); - let v: Vec = json::decode("[3, 1]").unwrap(); + let v: Vec = json::decode("[3, 1]"); assert_eq!(v, [3, 1]); - let v: Vec> = json::decode("[[3], [1, 2]]").unwrap(); + let v: Vec> = json::decode("[[3], [1, 2]]"); assert_eq!(v, [vec![3], vec![1, 2]]); } #[test] fn test_decode_tuple() { - let t: (usize, usize, usize) = json::decode("[1, 2, 3]").unwrap(); + let t: (usize, usize, usize) = json::decode("[1, 2, 3]"); assert_eq!(t, (1, 2, 3)); - let t: (usize, string::String) = json::decode("[1, \"two\"]").unwrap(); + let t: (usize, string::String) = json::decode("[1, \"two\"]"); assert_eq!(t, (1, "two".to_string())); } #[test] +#[should_panic] fn test_decode_tuple_malformed_types() { - assert!(json::decode::<(usize, string::String)>("[1, 2]").is_err()); + json::decode::<(usize, string::String)>("[1, 2]"); } #[test] +#[should_panic] fn test_decode_tuple_malformed_length() { - assert!(json::decode::<(usize, usize)>("[1, 2, 3]").is_err()); + json::decode::<(usize, usize)>("[1, 2, 3]"); } #[test] @@ -562,7 +560,7 @@ fn test_decode_struct() { ] }"; - let v: Outer = json::decode(s).unwrap(); + let v: Outer = json::decode(s); assert_eq!( v, Outer { inner: vec![Inner { a: (), b: 2, c: vec!["abc".to_string(), "xyz".to_string()] }] } @@ -577,7 +575,7 @@ struct FloatStruct { #[test] fn test_decode_struct_with_nan() { let s = "{\"f\":null,\"a\":[null,123]}"; - let obj: FloatStruct = json::decode(s).unwrap(); + let obj: FloatStruct = json::decode(s); assert!(obj.f.is_nan()); assert!(obj.a[0].is_nan()); assert_eq!(obj.a[1], 123f64); @@ -585,20 +583,20 @@ fn test_decode_struct_with_nan() { #[test] fn test_decode_option() { - let value: Option = json::decode("null").unwrap(); + let value: Option = json::decode("null"); assert_eq!(value, None); - let value: Option = json::decode("\"jodhpurs\"").unwrap(); + let value: Option = json::decode("\"jodhpurs\""); assert_eq!(value, Some("jodhpurs".to_string())); } #[test] fn test_decode_enum() { - let value: Animal = json::decode("\"Dog\"").unwrap(); + let value: Animal = json::decode("\"Dog\""); assert_eq!(value, Dog); let s = "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}"; - let value: Animal = json::decode(s).unwrap(); + let value: Animal = json::decode(s); assert_eq!(value, Frog("Henry".to_string(), 349)); } @@ -606,7 +604,7 @@ fn test_decode_enum() { fn test_decode_map() { let s = "{\"a\": \"Dog\", \"b\": {\"variant\":\"Frog\",\ \"fields\":[\"Henry\", 349]}}"; - let mut map: BTreeMap = json::decode(s).unwrap(); + let mut map: BTreeMap = json::decode(s); assert_eq!(map.remove(&"a".to_string()), Some(Dog)); assert_eq!(map.remove(&"b".to_string()), Some(Frog("Henry".to_string(), 349))); @@ -630,59 +628,65 @@ enum DecodeEnum { A(f64), B(string::String), } -fn check_err>(to_parse: &'static str, expected: DecoderError) { - let res: DecodeResult = match from_str(to_parse) { - Err(e) => Err(ParseError(e)), - Ok(json) => Decodable::decode(&mut Decoder::new(json)), - }; - match res { - Ok(_) => panic!("`{:?}` parsed & decoded ok, expecting error `{:?}`", to_parse, expected), - Err(ParseError(e)) => panic!("`{:?}` is not valid json: {:?}", to_parse, e), - Err(e) => { - assert_eq!(e, expected); - } - } +fn check_err>(to_parse: &str) { + let json = from_str(to_parse).unwrap(); + let _: T = Decodable::decode(&mut Decoder::new(json)); } #[test] -fn test_decode_errors_struct() { - check_err::("[]", ExpectedError("Object".to_string(), "[]".to_string())); - check_err::( - "{\"x\": true, \"y\": true, \"z\": \"\", \"w\": []}", - ExpectedError("Number".to_string(), "true".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": [], \"z\": \"\", \"w\": []}", - ExpectedError("Boolean".to_string(), "[]".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": {}, \"w\": []}", - ExpectedError("String".to_string(), "{}".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": \"\", \"w\": null}", - ExpectedError("Array".to_string(), "null".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": \"\"}", - MissingFieldError("w".to_string()), - ); +#[should_panic(expected = r#"ExpectedError("Object", "[]")"#)] +fn test_decode_errors_struct1() { + check_err::("[]"); } #[test] -fn test_decode_errors_enum() { - check_err::("{}", MissingFieldError("variant".to_string())); - check_err::( - "{\"variant\": 1}", - ExpectedError("String".to_string(), "1".to_string()), - ); - check_err::("{\"variant\": \"A\"}", MissingFieldError("fields".to_string())); - check_err::( - "{\"variant\": \"A\", \"fields\": null}", - ExpectedError("Array".to_string(), "null".to_string()), - ); - check_err::( - "{\"variant\": \"C\", \"fields\": []}", - UnknownVariantError("C".to_string()), - ); +#[should_panic(expected = r#"ExpectedError("Number", "true")"#)] +fn test_decode_errors_struct2() { + check_err::(r#"{"x": true, "y": true, "z": "", "w": []}"#); +} +#[test] +#[should_panic(expected = r#"ExpectedError("Boolean", "[]")"#)] +fn test_decode_errors_struct3() { + check_err::(r#"{"x": 1, "y": [], "z": "", "w": []}"#); +} +#[test] +#[should_panic(expected = r#"ExpectedError("String", "{}")"#)] +fn test_decode_errors_struct4() { + check_err::(r#"{"x": 1, "y": true, "z": {}, "w": []}"#); +} +#[test] +#[should_panic(expected = r#"ExpectedError("Array", "null")"#)] +fn test_decode_errors_struct5() { + check_err::(r#"{"x": 1, "y": true, "z": "", "w": null}"#); +} +#[test] +#[should_panic(expected = r#"ExpectedError("Array", "null")"#)] +fn test_decode_errors_struct6() { + check_err::(r#"{"x": 1, "y": true, "z": ""}"#); +} + +#[test] +#[should_panic(expected = r#"MissingFieldError("variant")"#)] +fn test_decode_errors_enum1() { + check_err::(r#"{}"#); +} +#[test] +#[should_panic(expected = r#"ExpectedError("String", "1")"#)] +fn test_decode_errors_enum2() { + check_err::(r#"{"variant": 1}"#); +} +#[test] +#[should_panic(expected = r#"MissingFieldError("fields")"#)] +fn test_decode_errors_enum3() { + check_err::(r#"{"variant": "A"}"#); +} +#[test] +#[should_panic(expected = r#"ExpectedError("Array", "null")"#)] +fn test_decode_errors_enum4() { + check_err::(r#"{"variant": "A", "fields": null}"#); +} +#[test] +#[should_panic(expected = r#"UnknownVariantError("C")"#)] +fn test_decode_errors_enum5() { + check_err::(r#"{"variant": "C", "fields": []}"#); } #[test] @@ -944,7 +948,7 @@ fn test_hashmap_with_enum_key() { map.insert(Enum::Foo, 0); let result = json::encode(&map).unwrap(); assert_eq!(&result[..], r#"{"Foo":0}"#); - let decoded: HashMap = json::decode(&result).unwrap(); + let decoded: HashMap = json::decode(&result); assert_eq!(map, decoded); } @@ -957,10 +961,11 @@ fn test_hashmap_with_numeric_key_can_handle_double_quote_delimited_key() { Ok(o) => o, }; let mut decoder = Decoder::new(json_obj); - let _hm: HashMap = Decodable::decode(&mut decoder).unwrap(); + let _hm: HashMap = Decodable::decode(&mut decoder); } #[test] +#[should_panic(expected = r#"ExpectedError("Number", "a")"#)] fn test_hashmap_with_numeric_key_will_error_with_string_keys() { use std::collections::HashMap; let json_str = "{\"a\":true}"; @@ -969,8 +974,7 @@ fn test_hashmap_with_numeric_key_will_error_with_string_keys() { Ok(o) => o, }; let mut decoder = Decoder::new(json_obj); - let result: Result, DecoderError> = Decodable::decode(&mut decoder); - assert_eq!(result, Err(ExpectedError("Number".to_string(), "a".to_string()))); + let _: HashMap = Decodable::decode(&mut decoder); } fn assert_stream_equal(src: &str, expected: Vec<(JsonEvent, Vec>)>) { diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs index 3e2aab5125..314c07db98 100644 --- a/compiler/rustc_serialize/tests/leb128.rs +++ b/compiler/rustc_serialize/tests/leb128.rs @@ -30,9 +30,8 @@ macro_rules! impl_test_unsigned_leb128 { let mut position = 0; for &expected in &values { - let (actual, bytes_read) = $read_fn_name(&stream[position..]); + let actual = $read_fn_name(&stream, &mut position); assert_eq!(expected, actual); - position += bytes_read; } assert_eq!(stream.len(), position); } @@ -77,9 +76,8 @@ macro_rules! impl_test_signed_leb128 { let mut position = 0; for &expected in &values { - let (actual, bytes_read) = $read_fn_name(&stream[position..]); + let actual = $read_fn_name(&stream, &mut position); assert_eq!(expected, actual); - position += bytes_read; } assert_eq!(stream.len(), position); } diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs index 13b3676a56..298eb11511 100644 --- a/compiler/rustc_serialize/tests/opaque.rs +++ b/compiler/rustc_serialize/tests/opaque.rs @@ -41,7 +41,7 @@ fn check_round_trip + for<'a> Decodable> + Par let mut decoder = Decoder::new(&data[..], 0); for value in values { - let decoded = Decodable::decode(&mut decoder).unwrap(); + let decoded = Decodable::decode(&mut decoder); assert_eq!(value, decoded); } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 08bcea26eb..7a0d9a212c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -12,11 +12,11 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::impl_stable_hash_via_hash; use rustc_target::abi::{Align, TargetDataLayout}; -use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple, TargetWarnings}; +use rustc_target::spec::{LinkerFlavor, SplitDebuginfo, Target, TargetTriple, TargetWarnings}; use rustc_serialize::json; -use crate::parse::CrateConfig; +use crate::parse::{CrateCheckConfig, CrateConfig}; use rustc_feature::UnstableFeatures; use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION}; use rustc_span::source_map::{FileName, FilePathMapping}; @@ -63,6 +63,22 @@ pub enum CFGuard { Checks, } +/// The different settings that the `-Z cf-protection` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CFProtection { + /// Do not enable control-flow protection + None, + + /// Emit control-flow protection for branches (enables indirect branch tracking). + Branch, + + /// Emit control-flow protection for returns. + Return, + + /// Emit control-flow protection for both branches and returns. + Full, +} + #[derive(Clone, Copy, Debug, PartialEq, Hash)] pub enum OptLevel { No, // -O0 @@ -127,16 +143,16 @@ pub enum MirSpanview { Block, } -/// The different settings that the `-Z instrument-coverage` flag can have. +/// The different settings that the `-C instrument-coverage` flag can have. /// -/// Coverage instrumentation now supports combining `-Z instrument-coverage` +/// Coverage instrumentation now supports combining `-C instrument-coverage` /// with compiler and linker optimization (enabled with `-O` or `-C opt-level=1` /// and higher). Nevertheless, there are many variables, depending on options /// selected, code structure, and enabled attributes. If errors are encountered, /// either while compiling or when generating `llvm-cov show` reports, consider /// lowering the optimization level, including or excluding `-C link-dead-code`, -/// or using `-Z instrument-coverage=except-unused-functions` or `-Z -/// instrument-coverage=except-unused-generics`. +/// or using `-Zunstable-options -C instrument-coverage=except-unused-functions` +/// or `-Zunstable-options -C instrument-coverage=except-unused-generics`. /// /// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the /// coverage map, it will not attempt to generate synthetic functions for unused @@ -148,13 +164,13 @@ pub enum MirSpanview { /// unless the function has type parameters. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum InstrumentCoverage { - /// Default `-Z instrument-coverage` or `-Z instrument-coverage=statement` + /// Default `-C instrument-coverage` or `-C instrument-coverage=statement` All, - /// `-Z instrument-coverage=except-unused-generics` + /// `-Zunstable-options -C instrument-coverage=except-unused-generics` ExceptUnusedGenerics, - /// `-Z instrument-coverage=except-unused-functions` + /// `-Zunstable-options -C instrument-coverage=except-unused-functions` ExceptUnusedFunctions, - /// `-Z instrument-coverage=off` (or `no`, etc.) + /// `-C instrument-coverage=off` (or `no`, etc.) Off, } @@ -565,6 +581,7 @@ pub enum PrintRequest { TargetSpec, NativeStaticLibs, StackProtectorStrategies, + LinkArgs, } #[derive(Copy, Clone)] @@ -769,7 +786,6 @@ impl Default for Options { externs: Externs(BTreeMap::new()), extern_dep_specs: ExternDepSpecs(BTreeMap::new()), crate_name: None, - alt_std_name: None, libs: Vec::new(), unstable_features: UnstableFeatures::Disallow, debug_assertions: true, @@ -891,11 +907,36 @@ impl Passes { } } +#[derive(Clone, Copy, Hash, Debug, PartialEq)] +pub enum PAuthKey { + A, + B, +} + +#[derive(Clone, Copy, Hash, Debug, PartialEq)] +pub struct PacRet { + pub leaf: bool, + pub key: PAuthKey, +} + +#[derive(Clone, Copy, Hash, Debug, PartialEq)] +pub struct BranchProtection { + pub bti: bool, + pub pac_ret: Option, +} + +impl Default for BranchProtection { + fn default() -> Self { + BranchProtection { bti: false, pac_ret: None } + } +} + pub const fn default_lib_output() -> CrateType { CrateType::Rlib } fn default_configuration(sess: &Session) -> CrateConfig { + // NOTE: This should be kept in sync with `CrateCheckConfig::fill_well_known` below. let end = &sess.target.endian; let arch = &sess.target.arch; let wordsz = sess.target.pointer_width.to_string(); @@ -980,6 +1021,91 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option)>) -> CrateConfig cfg.into_iter().map(|(a, b)| (Symbol::intern(&a), b.map(|b| Symbol::intern(&b)))).collect() } +/// The parsed `--check-cfg` options +pub struct CheckCfg { + /// Set if `names()` checking is enabled + pub names_checked: bool, + /// The union of all `names()` + pub names_valid: FxHashSet, + /// The set of names for which `values()` was used + pub values_checked: FxHashSet, + /// The set of all (name, value) pairs passed in `values()` + pub values_valid: FxHashSet<(T, T)>, +} + +impl Default for CheckCfg { + fn default() -> Self { + CheckCfg { + names_checked: false, + names_valid: FxHashSet::default(), + values_checked: FxHashSet::default(), + values_valid: FxHashSet::default(), + } + } +} + +impl CheckCfg { + fn map_data(&self, f: impl Fn(&T) -> O) -> CheckCfg { + CheckCfg { + names_checked: self.names_checked, + names_valid: self.names_valid.iter().map(|a| f(a)).collect(), + values_checked: self.values_checked.iter().map(|a| f(a)).collect(), + values_valid: self.values_valid.iter().map(|(a, b)| (f(a), f(b))).collect(), + } + } +} + +/// Converts the crate `--check-cfg` options from `String` to `Symbol`. +/// `rustc_interface::interface::Config` accepts this in the compiler configuration, +/// but the symbol interner is not yet set up then, so we must convert it later. +pub fn to_crate_check_config(cfg: CheckCfg) -> CrateCheckConfig { + cfg.map_data(|s| Symbol::intern(s)) +} + +impl CrateCheckConfig { + /// Fills a `CrateCheckConfig` with well-known configuration names. + pub fn fill_well_known(&mut self) { + // NOTE: This should be kept in sync with `default_configuration` + const WELL_KNOWN_NAMES: &[Symbol] = &[ + sym::unix, + sym::windows, + sym::target_os, + sym::target_family, + sym::target_arch, + sym::target_endian, + sym::target_pointer_width, + sym::target_env, + sym::target_abi, + sym::target_vendor, + sym::target_thread_local, + sym::target_has_atomic_load_store, + sym::target_has_atomic, + sym::target_has_atomic_equal_alignment, + sym::panic, + sym::sanitize, + sym::debug_assertions, + sym::proc_macro, + sym::test, + sym::doc, + sym::doctest, + sym::feature, + ]; + for &name in WELL_KNOWN_NAMES { + self.names_valid.insert(name); + } + } + + /// Fills a `CrateCheckConfig` with configuration names and values that are actually active. + pub fn fill_actual(&mut self, cfg: &CrateConfig) { + for &(k, v) in cfg { + self.names_valid.insert(k); + if let Some(v) = v { + self.values_valid.insert((k, v)); + } + } + } +} + pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig { // Combine the configuration requested by the session (command line) with // some default and generated configuration items. @@ -1123,6 +1249,7 @@ pub fn rustc_short_optgroups() -> Vec { vec![ opt::flag_s("h", "help", "Display this message"), opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), + opt::multi("", "check-cfg", "Provide list of valid cfg options for checking", "SPEC"), opt::multi_s( "L", "", @@ -1163,7 +1290,8 @@ pub fn rustc_short_optgroups() -> Vec { "Compiler information to print on stdout", "[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\ target-cpus|target-features|relocation-models|code-models|\ - tls-models|target-spec-json|native-static-libs|stack-protector-strategies]", + tls-models|target-spec-json|native-static-libs|stack-protector-strategies|\ + link-args]", ), opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), @@ -1595,6 +1723,7 @@ fn collect_print_requests( ); } } + "link-args" => PrintRequest::LinkArgs, req => early_error(error_format, &format!("unknown print request `{}`", req)), })); @@ -2102,12 +2231,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { check_thread_count(&debugging_opts, error_format); - let incremental = - if std::env::var_os("RUSTC_FORCE_INCREMENTAL").map(|v| v == "1").unwrap_or(false) { - cg.incremental.as_ref().map(PathBuf::from) - } else { - None - }; + let incremental = cg.incremental.as_ref().map(PathBuf::from); let assert_incr_state = parse_assert_incr_state(&debugging_opts.assert_incr_state, error_format); @@ -2173,18 +2297,44 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { _ => {} } - if debugging_opts.instrument_coverage.is_some() - && debugging_opts.instrument_coverage != Some(InstrumentCoverage::Off) - { + // Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes + // precedence. + match (cg.instrument_coverage, debugging_opts.instrument_coverage) { + (Some(ic_c), Some(ic_z)) if ic_c != ic_z => { + early_error( + error_format, + "incompatible values passed for `-C instrument-coverage` \ + and `-Z instrument-coverage`", + ); + } + (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {} + (Some(_), _) if !debugging_opts.unstable_options => { + early_error( + error_format, + "`-C instrument-coverage=except-*` requires `-Z unstable-options`", + ); + } + (None, None) => {} + (None, ic) => { + early_warn( + error_format, + "`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`", + ); + cg.instrument_coverage = ic; + } + _ => {} + } + + if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) { if cg.profile_generate.enabled() || cg.profile_use.is_some() { early_error( error_format, - "option `-Z instrument-coverage` is not compatible with either `-C profile-use` \ + "option `-C instrument-coverage` is not compatible with either `-C profile-use` \ or `-C profile-generate`", ); } - // `-Z instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent + // `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent // and reversible name mangling. Note, LLVM coverage tools can analyze coverage over // multiple runs, including some changes to source code; so mangled names must be consistent // across compilations. @@ -2193,7 +2343,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { Some(SymbolManglingVersion::Legacy) => { early_warn( error_format, - "-Z instrument-coverage requires symbol mangling version `v0`, \ + "-C instrument-coverage requires symbol mangling version `v0`, \ but `-C symbol-mangling-version=legacy` was specified", ); } @@ -2215,6 +2365,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } } + if cg.linker_flavor == Some(LinkerFlavor::L4Bender) + && !nightly_options::is_unstable_enabled(matches) + { + early_error( + error_format, + "`l4-bender` linker flavor is unstable, `-Z unstable-options` \ + flag must also be passed to explicitly use it", + ); + } + let prints = collect_print_requests(&mut cg, &mut debugging_opts, matches, error_format); let cg = cg; @@ -2324,7 +2484,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()), extern_dep_specs, crate_name, - alt_std_name: None, libs, debug_assertions, actually_rustdoc: false, @@ -2574,11 +2733,11 @@ impl PpMode { /// we have an opt-in scheme here, so one is hopefully forced to think about /// how the hash should be calculated when adding a new command-line argument. crate mod dep_tracking { - use super::LdImpl; use super::{ - CFGuard, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, LinkerPluginLto, - LocationDetail, LtoCli, OptLevel, OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, - SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths, + BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, + InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OptLevel, OutputType, + OutputTypes, Passes, SourceFileHashAlgorithm, SwitchWithOptPath, SymbolManglingVersion, + TrimmedDefPaths, }; use crate::lint; use crate::options::WasiExecModel; @@ -2659,6 +2818,7 @@ crate mod dep_tracking { NativeLibKind, SanitizerSet, CFGuard, + CFProtection, TargetTriple, Edition, LinkerPluginLto, @@ -2672,6 +2832,7 @@ crate mod dep_tracking { OutputType, RealFileName, LocationDetail, + BranchProtection, ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 357190178c..9200be363a 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -1,13 +1,11 @@ //! A module for searching for libraries -pub use self::FileMatch::*; - use std::env; use std::fs; use std::iter::FromIterator; use std::path::{Path, PathBuf}; -use crate::search_paths::{PathKind, SearchPath, SearchPathFile}; +use crate::search_paths::{PathKind, SearchPath}; use rustc_fs_util::fix_windows_verbatim_for_gcc; use tracing::debug; @@ -43,36 +41,6 @@ impl<'a> FileSearch<'a> { self.get_lib_path().join("self-contained") } - pub fn search(&self, mut pick: F) - where - F: FnMut(&SearchPathFile, PathKind) -> FileMatch, - { - for search_path in self.search_paths() { - debug!("searching {}", search_path.dir.display()); - fn is_rlib(spf: &SearchPathFile) -> bool { - if let Some(f) = &spf.file_name_str { f.ends_with(".rlib") } else { false } - } - // Reading metadata out of rlibs is faster, and if we find both - // an rlib and a dylib we only read one of the files of - // metadata, so in the name of speed, bring all rlib files to - // the front of the search list. - let files1 = search_path.files.iter().filter(|spf| is_rlib(&spf)); - let files2 = search_path.files.iter().filter(|spf| !is_rlib(&spf)); - for spf in files1.chain(files2) { - debug!("testing {}", spf.path.display()); - let maybe_picked = pick(spf, search_path.kind); - match maybe_picked { - FileMatches => { - debug!("picked {}", spf.path.display()); - } - FileDoesntMatch => { - debug!("rejected {}", spf.path.display()); - } - } - } - } - } - pub fn new( sysroot: &'a Path, triple: &'a str, diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 399b616915..c7a6ac1d4e 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -2,7 +2,9 @@ #![feature(derive_default_enum)] #![feature(min_specialization)] #![feature(once_cell)] +#![feature(option_get_or_insert_default)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c8ae005e21..658b07cced 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -109,17 +109,16 @@ impl Options { } pub fn instrument_coverage(&self) -> bool { - self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - != InstrumentCoverage::Off + self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off } pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) == InstrumentCoverage::ExceptUnusedGenerics } pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) == InstrumentCoverage::ExceptUnusedFunctions } } @@ -185,10 +184,6 @@ top_level_options!( externs: Externs [UNTRACKED], extern_dep_specs: ExternDepSpecs [UNTRACKED], crate_name: Option [TRACKED], - /// An optional name to use as the crate for std during std injection, - /// written `extern crate name as std`. Defaults to `std`. Used by - /// out-of-tree drivers. - alt_std_name: Option [TRACKED], /// Indicates how the compiler should treat unstable features. unstable_features: UnstableFeatures [TRACKED], @@ -381,10 +376,11 @@ mod desc { pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_opt_panic_strategy: &str = parse_panic_strategy; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory` or `thread`"; + pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, or `thread`"; pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; + pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)"; pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; @@ -417,6 +413,8 @@ mod desc { pub const parse_gcc_ld: &str = "one of: no value, `lld`"; pub const parse_stack_protector: &str = "one of (`none` (default), `basic`, `strong`, or `all`)"; + pub const parse_branch_protection: &str = + "a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`"; } mod parse { @@ -641,6 +639,7 @@ mod parse { "cfi" => SanitizerSet::CFI, "leak" => SanitizerSet::LEAK, "memory" => SanitizerSet::MEMORY, + "memtag" => SanitizerSet::MEMTAG, "thread" => SanitizerSet::THREAD, "hwaddress" => SanitizerSet::HWADDRESS, _ => return false, @@ -698,6 +697,25 @@ mod parse { true } + crate fn parse_cfprotection(slot: &mut CFProtection, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { CFProtection::Full } else { CFProtection::None }; + return true; + } + } + + *slot = match v { + None | Some("none") => CFProtection::None, + Some("branch") => CFProtection::Branch, + Some("return") => CFProtection::Return, + Some("full") => CFProtection::Full, + Some(_) => return false, + }; + true + } + crate fn parse_linker_flavor(slot: &mut Option, v: Option<&str>) -> bool { match v.and_then(LinkerFlavor::from_str) { Some(lf) => *slot = Some(lf), @@ -965,6 +983,33 @@ mod parse { } true } + + crate fn parse_branch_protection(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { + let slot = slot.get_or_insert_default(); + for opt in s.split(',') { + match opt { + "bti" => slot.bti = true, + "pac-ret" if slot.pac_ret.is_none() => { + slot.pac_ret = Some(PacRet { leaf: false, key: PAuthKey::A }) + } + "leaf" => match slot.pac_ret.as_mut() { + Some(pac) => pac.leaf = true, + _ => return false, + }, + "b-key" => match slot.pac_ret.as_mut() { + Some(pac) => pac.key = PAuthKey::B, + _ => return false, + }, + _ => return false, + }; + } + } + _ => return false, + } + true + } } options! { @@ -1003,6 +1048,14 @@ options! { "enable incremental compilation"), inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), + instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], + "instrument the generated code to support LLVM source-based code coverage \ + reports (note, the compiler build config must include `profiler = true`); \ + implies `-C symbol-mangling-version=v0`. Optional values are: + `=all` (implicit value) + `=except-unused-generics` + `=except-unused-functions` + `=off` (default)"), link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], @@ -1109,6 +1162,10 @@ options! { (default: no)"), borrowck: String = ("migrate".to_string(), parse_string, [UNTRACKED], "select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"), + branch_protection: Option = (None, parse_branch_protection, [TRACKED], + "set options for branch target identification and pointer authentication on AArch64"), + cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], + "instrument control-flow architecture protection"), cgu_partitioning_strategy: Option = (None, parse_opt_string, [TRACKED], "the codegen unit partitioning strategy to use"), chalk: bool = (false, parse_bool, [TRACKED], @@ -1131,9 +1188,13 @@ options! { dep_tasks: bool = (false, parse_bool, [UNTRACKED], "print tasks that execute and the color their dep node gets (requires debug build) \ (default: no)"), + dlltool: Option = (None, parse_opt_pathbuf, [UNTRACKED], + "import library generation tool (windows-gnu only)"), dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \ (default: no)"), + drop_tracking: bool = (false, parse_bool, [TRACKED], + "enables drop tracking in generators (default: no)"), dual_proc_macros: bool = (false, parse_bool, [TRACKED], "load proc macros for both target and host, but only link to the target (default: no)"), dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], @@ -1307,8 +1368,6 @@ options! { See #77382 and #74551."), print_fuel: Option = (None, parse_opt_string, [TRACKED], "make rustc print the total optimization fuel used by a crate"), - print_link_args: bool = (false, parse_bool, [UNTRACKED], - "print the arguments passed to the linker (default: no)"), print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], @@ -1330,8 +1389,6 @@ options! { "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), - query_stats: bool = (false, parse_bool, [UNTRACKED], - "print some statistics about the query system (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), layout_seed: Option = (None, parse_opt_number, [TRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index d5b520325e..7113f9b0a2 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -1,6 +1,7 @@ //! Contains `ParseSess` which holds state living beyond what one `Parser` might. //! It also serves as an input to the parser itself. +use crate::config::CheckCfg; use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId}; use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -18,6 +19,7 @@ use std::str; /// The set of keys (and, optionally, values) that define the compilation /// environment of the crate, used to drive conditional compilation. pub type CrateConfig = FxHashSet<(Symbol, Option)>; +pub type CrateCheckConfig = CheckCfg; /// Collected spans during parsing for places where a certain feature was /// used and should be feature gated accordingly in `check_crate`. @@ -117,6 +119,7 @@ pub struct ParseSess { pub span_diagnostic: Handler, pub unstable_features: UnstableFeatures, pub config: CrateConfig, + pub check_config: CrateCheckConfig, pub edition: Edition, pub missing_fragment_specifiers: Lock>, /// Places where raw identifiers were used. This is used to avoid complaining about idents @@ -162,6 +165,7 @@ impl ParseSess { span_diagnostic: handler, unstable_features: UnstableFeatures::from_environment(None), config: FxHashSet::default(), + check_config: CrateCheckConfig::default(), edition: ExpnId::root().expn_data().edition, missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index acb6c735e0..b6bde28233 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -15,22 +15,15 @@ pub struct SearchPath { /// doable, but very slow, because it involves calls to `file_name` and /// `extension` that are themselves slow. /// -/// This type augments the `PathBuf` with an `Option` containing the +/// This type augments the `PathBuf` with an `String` containing the /// `PathBuf`'s filename. The prefix and suffix checking is much faster on the -/// `Option` than the `PathBuf`. (It's an `Option` because -/// `Path::file_name` can fail; if that happens then all subsequent checking -/// will also fail, which is fine.) +/// `String` than the `PathBuf`. (The filename must be valid UTF-8. If it's +/// not, the entry should be skipped, because all Rust output files are valid +/// UTF-8, and so a non-UTF-8 filename couldn't be one we're looking for.) #[derive(Clone, Debug)] pub struct SearchPathFile { pub path: PathBuf, - pub file_name_str: Option, -} - -impl SearchPathFile { - fn new(path: PathBuf) -> SearchPathFile { - let file_name_str = path.file_name().and_then(|f| f.to_str()).map(|s| s.to_string()); - SearchPathFile { path, file_name_str } - } + pub file_name_str: String, } #[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable)] @@ -85,7 +78,14 @@ impl SearchPath { // Get the files within the directory. let files = match std::fs::read_dir(&dir) { Ok(files) => files - .filter_map(|e| e.ok().map(|e| SearchPathFile::new(e.path()))) + .filter_map(|e| { + e.ok().and_then(|e| { + e.file_name().to_str().map(|s| SearchPathFile { + path: e.path(), + file_name_str: s.to_string(), + }) + }) + }) .collect::>(), Err(..) => vec![], }; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 730e79a564..9bcdd7f3da 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -476,10 +476,6 @@ impl Session { &self.parse_sess.span_diagnostic } - pub fn with_disabled_diagnostic T>(&self, f: F) -> T { - self.parse_sess.span_diagnostic.with_disabled_diagnostic(f) - } - /// Analogous to calling methods on the given `DiagnosticBuilder`, but /// deduplicates on lint ID, span (if any), and message for this `Session` fn diag_once<'a, 'b>( diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 781fb8c1e5..7227b193f2 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -16,6 +16,6 @@ scoped-tls = "1.0" unicode-width = "0.1.4" cfg-if = "0.1.2" tracing = "0.1" -sha1 = { package = "sha-1", version = "0.9" } -sha2 = "0.9" -md5 = { package = "md-5", version = "0.9" } +sha1 = { package = "sha-1", version = "0.10.0" } +sha2 = "0.10.1" +md5 = { package = "md-5", version = "0.10.0" } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 24d2a8ac07..147c1f9e04 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -47,8 +47,8 @@ impl Encodable for CrateNum { } impl Decodable for CrateNum { - default fn decode(d: &mut D) -> Result { - Ok(CrateNum::from_u32(d.read_u32()?)) + default fn decode(d: &mut D) -> CrateNum { + CrateNum::from_u32(d.read_u32()) } } @@ -176,9 +176,9 @@ impl StableCrateId { // and no -Cmetadata, symbols from the same crate compiled with different versions of // rustc are named the same. // - // RUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER is used to inject rustc version information + // RUSTC_FORCE_RUSTC_VERSION is used to inject rustc version information // during testing. - if let Some(val) = std::env::var_os("RUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER") { + if let Some(val) = std::env::var_os("RUSTC_FORCE_RUSTC_VERSION") { hasher.write(val.to_string_lossy().into_owned().as_bytes()) } else { hasher.write(option_env!("CFG_VERSION").unwrap_or("unknown version").as_bytes()); @@ -209,7 +209,7 @@ impl Encodable for DefIndex { } impl Decodable for DefIndex { - default fn decode(_: &mut D) -> Result { + default fn decode(_: &mut D) -> DefIndex { panic!("cannot decode `DefIndex` with `{}`", std::any::type_name::()); } } @@ -221,10 +221,17 @@ impl Decodable for DefIndex { #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] // On below-64 bit systems we can simply use the derived `Hash` impl #[cfg_attr(not(target_pointer_width = "64"), derive(Hash))] -// Note that the order is essential here, see below why +#[repr(C)] +// We guarantee field order. Note that the order is essential here, see below why. pub struct DefId { + // cfg-ing the order of fields so that the `DefIndex` which is high entropy always ends up in + // the lower bits no matter the endianness. This allows the compiler to turn that `Hash` impl + // into a direct call to 'u64::hash(_)`. + #[cfg(not(all(target_pointer_width = "64", target_endian = "big")))] pub index: DefIndex, pub krate: CrateNum, + #[cfg(all(target_pointer_width = "64", target_endian = "big"))] + pub index: DefIndex, } // On 64-bit systems, we can hash the whole `DefId` as one `u64` instead of two `u32`s. This @@ -291,12 +298,10 @@ impl Encodable for DefId { } impl Decodable for DefId { - default fn decode(d: &mut D) -> Result { - d.read_struct(|d| { - Ok(DefId { - krate: d.read_struct_field("krate", Decodable::decode)?, - index: d.read_struct_field("index", Decodable::decode)?, - }) + default fn decode(d: &mut D) -> DefId { + d.read_struct(|d| DefId { + krate: d.read_struct_field("krate", Decodable::decode), + index: d.read_struct_field("index", Decodable::decode), }) } } @@ -371,8 +376,8 @@ impl Encodable for LocalDefId { } impl Decodable for LocalDefId { - fn decode(d: &mut D) -> Result { - DefId::decode(d).map(|d| d.expect_local()) + fn decode(d: &mut D) -> LocalDefId { + DefId::decode(d).expect_local() } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 315b706fbc..8265eb23c3 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -32,6 +32,7 @@ use crate::{HashStableContext, Span, DUMMY_SP}; use crate::def_id::{CrateNum, DefId, StableCrateId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_data_structures::unhash::UnhashMap; @@ -88,6 +89,33 @@ rustc_index::newtype_index! { } } +/// Assert that the provided `HashStableContext` is configured with the 'default' +/// `HashingControls`. We should always have bailed out before getting to here +/// with a non-default mode. With this check in place, we can avoid the need +/// to maintain separate versions of `ExpnData` hashes for each permutation +/// of `HashingControls` settings. +fn assert_default_hashing_controls(ctx: &CTX, msg: &str) { + match ctx.hashing_controls() { + // Ideally, we would also check that `node_id_hashing_mode` was always + // `NodeIdHashingMode::HashDefPath`. However, we currently end up hashing + // `Span`s in this mode, and there's not an easy way to change that. + // All of the span-related data that we hash is pretty self-contained + // (in particular, we don't hash any `HirId`s), so this shouldn't result + // in any caching problems. + // FIXME: Enforce that we don't end up transitively hashing any `HirId`s, + // or ensure that this method is always invoked with the same + // `NodeIdHashingMode` + // + // Note that we require that `hash_spans` be set according to the global + // `-Z incremental-ignore-spans` option. Normally, this option is disabled, + // which will cause us to require that this method always be called with `Span` hashing + // enabled. + HashingControls { hash_spans, node_id_hashing_mode: _ } + if hash_spans == !ctx.debug_opts_incremental_ignore_spans() => {} + other => panic!("Attempted hashing of {msg} with non-default HashingControls: {:?}", other), + } +} + /// A unique hash value associated to an expansion. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] pub struct ExpnHash(Fingerprint); @@ -144,10 +172,12 @@ impl LocalExpnId { /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. pub const ROOT: LocalExpnId = LocalExpnId::from_u32(0); + #[inline] pub fn from_raw(idx: ExpnIndex) -> LocalExpnId { LocalExpnId::from_u32(idx.as_u32()) } + #[inline] pub fn as_raw(self) -> ExpnIndex { ExpnIndex::from_u32(self.as_u32()) } @@ -1286,19 +1316,16 @@ pub fn decode_expn_id( // to track which `SyntaxContext`s we have already decoded. // The provided closure will be invoked to deserialize a `SyntaxContextData` // if we haven't already seen the id of the `SyntaxContext` we are deserializing. -pub fn decode_syntax_context< - D: Decoder, - F: FnOnce(&mut D, u32) -> Result, ->( +pub fn decode_syntax_context SyntaxContextData>( d: &mut D, context: &HygieneDecodeContext, decode_data: F, -) -> Result { - let raw_id: u32 = Decodable::decode(d)?; +) -> SyntaxContext { + let raw_id: u32 = Decodable::decode(d); if raw_id == 0 { debug!("decode_syntax_context: deserialized root"); // The root is special - return Ok(SyntaxContext::root()); + return SyntaxContext::root(); } let outer_ctxts = &context.remapped_ctxts; @@ -1306,7 +1333,7 @@ pub fn decode_syntax_context< // Ensure that the lock() temporary is dropped early { if let Some(ctxt) = outer_ctxts.lock().get(raw_id as usize).copied().flatten() { - return Ok(ctxt); + return ctxt; } } @@ -1336,7 +1363,7 @@ pub fn decode_syntax_context< // Don't try to decode data while holding the lock, since we need to // be able to recursively decode a SyntaxContext - let mut ctxt_data = decode_data(d, raw_id)?; + let mut ctxt_data = decode_data(d, raw_id); // Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names` // We don't care what the encoding crate set this to - we want to resolve it // from the perspective of the current compilation session @@ -1352,7 +1379,7 @@ pub fn decode_syntax_context< assert_eq!(dummy.dollar_crate_name, kw::Empty); }); - Ok(new_ctxt) + new_ctxt } fn for_all_ctxts_in Result<(), E>>( @@ -1394,13 +1421,13 @@ impl Encodable for ExpnId { } impl Decodable for LocalExpnId { - fn decode(d: &mut D) -> Result { - ExpnId::decode(d).map(ExpnId::expect_local) + fn decode(d: &mut D) -> Self { + ExpnId::expect_local(ExpnId::decode(d)) } } impl Decodable for ExpnId { - default fn decode(_: &mut D) -> Result { + default fn decode(_: &mut D) -> Self { panic!("cannot decode `ExpnId` with `{}`", std::any::type_name::()); } } @@ -1423,7 +1450,7 @@ impl Encodable for SyntaxContext { } impl Decodable for SyntaxContext { - default fn decode(_: &mut D) -> Result { + default fn decode(_: &mut D) -> Self { panic!("cannot decode `SyntaxContext` with `{}`", std::any::type_name::()); } } @@ -1444,6 +1471,7 @@ fn update_disambiguator(expn_data: &mut ExpnData, mut ctx: impl HashStableContex "Already set disambiguator for ExpnData: {:?}", expn_data ); + assert_default_hashing_controls(&ctx, "ExpnData (disambiguator)"); let mut expn_hash = expn_data.hash_expn(&mut ctx); let disambiguator = HygieneData::with(|data| { @@ -1493,6 +1521,7 @@ impl HashStable for SyntaxContext { impl HashStable for ExpnId { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + assert_default_hashing_controls(ctx, "ExpnId"); let hash = if *self == ExpnId::root() { // Avoid fetching TLS storage for a trivial often-used value. Fingerprint::ZERO diff --git a/compiler/rustc_span/src/lev_distance.rs b/compiler/rustc_span/src/lev_distance.rs index aed699e483..93cf965f10 100644 --- a/compiler/rustc_span/src/lev_distance.rs +++ b/compiler/rustc_span/src/lev_distance.rs @@ -11,16 +11,21 @@ use std::cmp; mod tests; /// 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() { - return b.chars().count(); - } else if b.is_empty() { - return a.chars().count(); +/// +/// Returns None if the distance exceeds the limit. +pub fn lev_distance(a: &str, b: &str, limit: usize) -> Option { + let n = a.chars().count(); + let m = b.chars().count(); + let min_dist = if n < m { m - n } else { n - m }; + + if min_dist > limit { + return None; + } + if n == 0 || m == 0 { + return (min_dist <= limit).then_some(min_dist); } - let mut dcol: Vec<_> = (0..=b.len()).collect(); - let mut t_last = 0; + let mut dcol: Vec<_> = (0..=m).collect(); for (i, sc) in a.chars().enumerate() { let mut current = i; @@ -35,10 +40,10 @@ pub fn lev_distance(a: &str, b: &str) -> usize { dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1; } current = next; - t_last = j; } } - dcol[t_last + 1] + + (dcol[m] <= limit).then_some(dcol[m]) } /// Finds the best match for a given word in the given iterator. @@ -51,39 +56,38 @@ pub fn lev_distance(a: &str, b: &str) -> usize { /// on an edge case with a lower(upper)case letters mismatch. #[cold] pub fn find_best_match_for_name( - name_vec: &[Symbol], + candidates: &[Symbol], lookup: Symbol, dist: Option, ) -> Option { let lookup = lookup.as_str(); - let max_dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3); + let lookup_uppercase = lookup.to_uppercase(); // Priority of matches: // 1. Exact case insensitive match // 2. Levenshtein distance match // 3. Sorted word match - if let Some(case_insensitive_match) = - name_vec.iter().find(|candidate| candidate.as_str().to_uppercase() == lookup.to_uppercase()) - { - return Some(*case_insensitive_match); + if let Some(c) = candidates.iter().find(|c| c.as_str().to_uppercase() == lookup_uppercase) { + return Some(*c); } - let levenshtein_match = name_vec - .iter() - .filter_map(|&name| { - let dist = lev_distance(lookup, name.as_str()); - if dist <= max_dist { Some((name, dist)) } else { None } - }) - // Here we are collecting the next structure: - // (levenshtein_match, levenshtein_distance) - .fold(None, |result, (candidate, dist)| match result { - None => Some((candidate, dist)), - Some((c, d)) => Some(if dist < d { (candidate, dist) } else { (c, d) }), - }); - if levenshtein_match.is_some() { - levenshtein_match.map(|(candidate, _)| candidate) - } else { - find_match_by_sorted_words(name_vec, lookup) + + let mut dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3); + let mut best = None; + for c in candidates { + match lev_distance(lookup, c.as_str(), dist) { + Some(0) => return Some(*c), + Some(d) => { + dist = d - 1; + best = Some(*c); + } + None => {} + } } + if best.is_some() { + return best; + } + + find_match_by_sorted_words(candidates, lookup) } fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option { diff --git a/compiler/rustc_span/src/lev_distance/tests.rs b/compiler/rustc_span/src/lev_distance/tests.rs index b32f8d32c1..4e34219248 100644 --- a/compiler/rustc_span/src/lev_distance/tests.rs +++ b/compiler/rustc_span/src/lev_distance/tests.rs @@ -5,18 +5,26 @@ fn test_lev_distance() { use std::char::{from_u32, MAX}; // Test bytelength agnosticity for c in (0..MAX as u32).filter_map(from_u32).map(|i| i.to_string()) { - assert_eq!(lev_distance(&c[..], &c[..]), 0); + assert_eq!(lev_distance(&c[..], &c[..], usize::MAX), Some(0)); } let a = "\nMäry häd ä little lämb\n\nLittle lämb\n"; let b = "\nMary häd ä little lämb\n\nLittle lämb\n"; let c = "Mary häd ä little lämb\n\nLittle lämb\n"; - assert_eq!(lev_distance(a, b), 1); - assert_eq!(lev_distance(b, a), 1); - assert_eq!(lev_distance(a, c), 2); - assert_eq!(lev_distance(c, a), 2); - assert_eq!(lev_distance(b, c), 1); - assert_eq!(lev_distance(c, b), 1); + assert_eq!(lev_distance(a, b, usize::MAX), Some(1)); + assert_eq!(lev_distance(b, a, usize::MAX), Some(1)); + assert_eq!(lev_distance(a, c, usize::MAX), Some(2)); + assert_eq!(lev_distance(c, a, usize::MAX), Some(2)); + assert_eq!(lev_distance(b, c, usize::MAX), Some(1)); + assert_eq!(lev_distance(c, b, usize::MAX), Some(1)); +} + +#[test] +fn test_lev_distance_limit() { + assert_eq!(lev_distance("abc", "abcd", 1), Some(1)); + assert_eq!(lev_distance("abc", "abcd", 0), None); + assert_eq!(lev_distance("abc", "xyz", 3), Some(3)); + assert_eq!(lev_distance("abc", "xyz", 2), None); } #[test] diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 3bbf2a0e45..dea2fbf04b 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -15,11 +15,13 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] +#![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(negative_impls)] #![feature(nll)] #![feature(min_specialization)] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_macros; @@ -42,6 +44,7 @@ pub mod hygiene; use hygiene::Transparency; pub use hygiene::{DesugaringKind, ExpnKind, MacroKind}; pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext}; +use rustc_data_structures::stable_hasher::HashingControls; pub mod def_id; use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, LOCAL_CRATE}; pub mod lev_distance; @@ -65,8 +68,8 @@ use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; +use md5::Digest; use md5::Md5; -use sha1::Digest; use sha1::Sha1; use sha2::Sha256; @@ -610,7 +613,7 @@ impl Span { #[inline] /// Returns `true` if `hi == lo`. - pub fn is_empty(&self) -> bool { + pub fn is_empty(self) -> bool { let span = self.data_untracked(); span.hi == span.lo } @@ -638,7 +641,7 @@ impl Span { /// /// Use this instead of `==` when either span could be generated code, /// and you only care that they point to the same bytes of source text. - pub fn source_equal(&self, other: &Span) -> bool { + pub fn source_equal(self, other: Span) -> bool { let span = self.data(); let other = other.data(); span.lo == other.lo && span.hi == other.hi @@ -679,17 +682,17 @@ impl Span { } #[inline] - pub fn rust_2015(&self) -> bool { + pub fn rust_2015(self) -> bool { self.edition() == edition::Edition::Edition2015 } #[inline] - pub fn rust_2018(&self) -> bool { + pub fn rust_2018(self) -> bool { self.edition() >= edition::Edition::Edition2018 } #[inline] - pub fn rust_2021(&self) -> bool { + pub fn rust_2021(self) -> bool { self.edition() >= edition::Edition::Edition2021 } @@ -710,7 +713,7 @@ impl Span { /// Checks if a span is "internal" to a macro in which `#[unstable]` /// items can be used (that is, a macro marked with /// `#[allow_internal_unstable]`). - pub fn allows_unstable(&self, feature: Symbol) -> bool { + pub fn allows_unstable(self, feature: Symbol) -> bool { self.ctxt() .outer_expn_data() .allow_internal_unstable @@ -718,7 +721,7 @@ impl Span { } /// Checks if this span arises from a compiler desugaring of kind `kind`. - pub fn is_desugaring(&self, kind: DesugaringKind) -> bool { + pub fn is_desugaring(self, kind: DesugaringKind) -> bool { match self.ctxt().outer_expn_data().kind { ExpnKind::Desugaring(k) => k == kind, _ => false, @@ -727,7 +730,7 @@ impl Span { /// Returns the compiler desugaring that created this span, or `None` /// if this span is not from a desugaring. - pub fn desugaring_kind(&self) -> Option { + pub fn desugaring_kind(self) -> Option { match self.ctxt().outer_expn_data().kind { ExpnKind::Desugaring(k) => Some(k), _ => None, @@ -737,7 +740,7 @@ impl Span { /// Checks if a span is "internal" to a macro in which `unsafe` /// can be used without triggering the `unsafe_code` lint. // (that is, a macro marked with `#[allow_internal_unsafe]`). - pub fn allows_unsafe(&self) -> bool { + pub fn allows_unsafe(self) -> bool { self.ctxt().outer_expn_data().allow_internal_unsafe } @@ -750,7 +753,7 @@ impl Span { return None; } - let is_recursive = expn_data.call_site.source_equal(&prev_span); + let is_recursive = expn_data.call_site.source_equal(prev_span); prev_span = self; self = expn_data.call_site; @@ -864,13 +867,13 @@ impl Span { /// Equivalent of `Span::call_site` from the proc macro API, /// except that the location is taken from the `self` span. - pub fn with_call_site_ctxt(&self, expn_id: ExpnId) -> Span { + pub fn with_call_site_ctxt(self, expn_id: ExpnId) -> Span { self.with_ctxt_from_mark(expn_id, Transparency::Transparent) } /// Equivalent of `Span::mixed_site` from the proc macro API, /// except that the location is taken from the `self` span. - pub fn with_mixed_site_ctxt(&self, expn_id: ExpnId) -> Span { + pub fn with_mixed_site_ctxt(self, expn_id: ExpnId) -> Span { self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent) } @@ -974,12 +977,12 @@ impl Encodable for Span { } } impl Decodable for Span { - default fn decode(s: &mut D) -> Result { + default fn decode(s: &mut D) -> Span { s.read_struct(|d| { - let lo = d.read_struct_field("lo", Decodable::decode)?; - let hi = d.read_struct_field("hi", Decodable::decode)?; + let lo = d.read_struct_field("lo", Decodable::decode); + let hi = d.read_struct_field("hi", Decodable::decode); - Ok(Span::new(lo, hi, SyntaxContext::root(), None)) + Span::new(lo, hi, SyntaxContext::root(), None) }) } } @@ -1010,37 +1013,25 @@ pub fn with_source_map T>(source_map: Lrc, f: F) -> f() } -pub fn debug_with_source_map( - span: Span, - f: &mut fmt::Formatter<'_>, - source_map: &SourceMap, -) -> fmt::Result { - write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(span), span.ctxt()) -} - -pub fn default_span_debug(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { - with_session_globals(|session_globals| { - if let Some(source_map) = &*session_globals.source_map.borrow() { - debug_with_source_map(span, f, source_map) - } else { - f.debug_struct("Span") - .field("lo", &span.lo()) - .field("hi", &span.hi()) - .field("ctxt", &span.ctxt()) - .finish() - } - }) -} - impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*SPAN_DEBUG)(*self, f) + with_session_globals(|session_globals| { + if let Some(source_map) = &*session_globals.source_map.borrow() { + write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(*self), self.ctxt()) + } else { + f.debug_struct("Span") + .field("lo", &self.lo()) + .field("hi", &self.hi()) + .field("ctxt", &self.ctxt()) + .finish() + } + }) } } impl fmt::Debug for SpanData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*SPAN_DEBUG)(Span::new(self.lo, self.hi, self.ctxt, self.parent), f) + fmt::Debug::fmt(&Span::new(self.lo, self.hi, self.ctxt, self.parent), f) } } @@ -1447,30 +1438,30 @@ impl Encodable for SourceFile { } impl Decodable for SourceFile { - fn decode(d: &mut D) -> Result { + fn decode(d: &mut D) -> SourceFile { d.read_struct(|d| { - let name: FileName = d.read_struct_field("name", |d| Decodable::decode(d))?; + let name: FileName = d.read_struct_field("name", |d| Decodable::decode(d)); let src_hash: SourceFileHash = - d.read_struct_field("src_hash", |d| Decodable::decode(d))?; - let start_pos: BytePos = d.read_struct_field("start_pos", |d| Decodable::decode(d))?; - let end_pos: BytePos = d.read_struct_field("end_pos", |d| Decodable::decode(d))?; + d.read_struct_field("src_hash", |d| Decodable::decode(d)); + let start_pos: BytePos = d.read_struct_field("start_pos", |d| Decodable::decode(d)); + let end_pos: BytePos = d.read_struct_field("end_pos", |d| Decodable::decode(d)); let lines: Vec = d.read_struct_field("lines", |d| { - let num_lines: u32 = Decodable::decode(d)?; + let num_lines: u32 = Decodable::decode(d); let mut lines = Vec::with_capacity(num_lines as usize); if num_lines > 0 { // Read the number of bytes used per diff. - let bytes_per_diff: u8 = Decodable::decode(d)?; + let bytes_per_diff: u8 = Decodable::decode(d); // Read the first element. - let mut line_start: BytePos = Decodable::decode(d)?; + let mut line_start: BytePos = Decodable::decode(d); lines.push(line_start); for _ in 1..num_lines { let diff = match bytes_per_diff { - 1 => d.read_u8()? as u32, - 2 => d.read_u16()? as u32, - 4 => d.read_u32()?, + 1 => d.read_u8() as u32, + 2 => d.read_u16() as u32, + 4 => d.read_u32(), _ => unreachable!(), }; @@ -1480,17 +1471,17 @@ impl Decodable for SourceFile { } } - Ok(lines) - })?; + lines + }); let multibyte_chars: Vec = - d.read_struct_field("multibyte_chars", |d| Decodable::decode(d))?; + d.read_struct_field("multibyte_chars", |d| Decodable::decode(d)); let non_narrow_chars: Vec = - d.read_struct_field("non_narrow_chars", |d| Decodable::decode(d))?; - let name_hash: u128 = d.read_struct_field("name_hash", |d| Decodable::decode(d))?; + d.read_struct_field("non_narrow_chars", |d| Decodable::decode(d)); + let name_hash: u128 = d.read_struct_field("name_hash", |d| Decodable::decode(d)); let normalized_pos: Vec = - d.read_struct_field("normalized_pos", |d| Decodable::decode(d))?; - let cnum: CrateNum = d.read_struct_field("cnum", |d| Decodable::decode(d))?; - Ok(SourceFile { + d.read_struct_field("normalized_pos", |d| Decodable::decode(d)); + let cnum: CrateNum = d.read_struct_field("cnum", |d| Decodable::decode(d)); + SourceFile { name, start_pos, end_pos, @@ -1505,7 +1496,7 @@ impl Decodable for SourceFile { normalized_pos, name_hash, cnum, - }) + } }) } } @@ -1948,8 +1939,8 @@ impl Encodable for BytePos { } impl Decodable for BytePos { - fn decode(d: &mut D) -> Result { - Ok(BytePos(d.read_u32()?)) + fn decode(d: &mut D) -> BytePos { + BytePos(d.read_u32()) } } @@ -2000,8 +1991,6 @@ pub struct FileLines { pub lines: Vec, } -pub static SPAN_DEBUG: AtomicRef) -> fmt::Result> = - AtomicRef::new(&(default_span_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); pub static SPAN_TRACK: AtomicRef = AtomicRef::new(&((|_| {}) as fn(_))); // _____________________________________________________________________________ @@ -2057,11 +2046,15 @@ impl InnerSpan { pub trait HashStableContext { fn def_path_hash(&self, def_id: DefId) -> DefPathHash; fn hash_spans(&self) -> bool; + /// Accesses `sess.opts.debugging_opts.incremental_ignore_spans` since + /// we don't have easy access to a `Session` + fn debug_opts_incremental_ignore_spans(&self) -> bool; fn def_span(&self, def_id: LocalDefId) -> Span; fn span_data_to_lines_and_cols( &mut self, span: &SpanData, ) -> Option<(Lrc, usize, BytePos, usize, BytePos)>; + fn hashing_controls(&self) -> HashingControls; } impl HashStable for Span diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 7414d201f5..95177102dc 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -629,26 +629,41 @@ impl SourceMap { } /// Extends the given `Span` to just after the previous occurrence of `pat` when surrounded by - /// whitespace. Returns 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_str(&self, sp: Span, pat: &str, accept_newlines: bool) -> Span { + /// whitespace. Returns None if the pattern could not be found or if an error occurred while + /// retrieving the code snippet. + pub fn span_extend_to_prev_str( + &self, + sp: Span, + pat: &str, + accept_newlines: bool, + include_whitespace: bool, + ) -> Option { // assure that the pattern is delimited, to avoid the following // fn my_fn() // ^^^^ returned span without the check // ---------- correct span + let prev_source = self.span_to_prev_source(sp).ok()?; for ws in &[" ", "\t", "\n"] { 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() && sp.lo().0 != 0 { - return sp.with_lo(BytePos(sp.lo().0 - 1)); - } else if accept_newlines || !prev_source.contains('\n') { - return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32)); + if let Some(pat_pos) = prev_source.rfind(&pat) { + let just_after_pat_pos = pat_pos + pat.len() - 1; + let just_after_pat_plus_ws = if include_whitespace { + just_after_pat_pos + + prev_source[just_after_pat_pos..] + .find(|c: char| !c.is_whitespace()) + .unwrap_or(0) + } else { + just_after_pat_pos + }; + let len = prev_source.len() - just_after_pat_plus_ws; + let prev_source = &prev_source[just_after_pat_plus_ws..]; + if accept_newlines || !prev_source.trim_start().contains('\n') { + return Some(sp.with_lo(BytePos(sp.lo().0 - len as u32))); } } } - sp + None } /// Returns the source snippet as `String` after the given `Span`. @@ -927,7 +942,7 @@ impl SourceMap { } pub fn generate_fn_name_span(&self, span: Span) -> Option { - let prev_span = self.span_extend_to_prev_str(span, "fn", true); + let prev_span = self.span_extend_to_prev_str(span, "fn", true, true).unwrap_or(span); if let Ok(snippet) = self.span_to_snippet(prev_span) { debug!( "generate_fn_name_span: span={:?}, prev_span={:?}, snippet={:?}", @@ -968,8 +983,7 @@ impl SourceMap { pub fn generate_local_type_param_snippet(&self, span: Span) -> Option<(Span, String)> { // Try to extend the span to the previous "fn" keyword to retrieve the function // signature. - let sugg_span = self.span_extend_to_prev_str(span, "fn", false); - if sugg_span != span { + if let Some(sugg_span) = self.span_extend_to_prev_str(span, "fn", false, true) { if let Ok(snippet) = self.span_to_snippet(sugg_span) { // Consume the function name. let mut offset = snippet diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index e9120b98aa..61e4074a7c 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -61,6 +61,15 @@ use rustc_data_structures::fx::FxIndexSet; /// using the callback `SPAN_TRACK` to access the query engine. /// #[derive(Clone, Copy, Eq, PartialEq, Hash)] +// FIXME(@lcnr): Enable this attribute once the bootstrap +// compiler knows of `rustc_pass_by_value`. +// +// Right now, this lint would only trigger when compiling the +// stage 2 compiler, which is fairly annoying as there are +// a lot of places using `&Span` right now. After the next bootstrap bump, +// the lint will already trigger when using stage 1, which is a lot less annoying. +// +// #[cfg_attr(not(bootstrap), rustc_pass_by_value)] pub struct Span { base_or_index: u32, len_or_tag: u16, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e008224ec0..c746255e95 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -184,6 +184,7 @@ symbols! { Formatter, From, FromIterator, + FromResidual, Future, FxHashMap, FxHashSet, @@ -208,6 +209,7 @@ symbols! { LinkedList, LintPass, Mutex, + N, None, Ok, Option, @@ -271,7 +273,6 @@ symbols! { __H, __S, __try_var, - _args, _d, _e, _task_context, @@ -315,18 +316,22 @@ symbols! { allow_internal_unsafe, allow_internal_unstable, allowed, + alu32, always, and, and_then, any, + append_const_msg, arbitrary_enum_discriminant, arbitrary_self_types, + args, arith_offset, arm, arm_target_feature, array, arrays, as_ptr, + as_ref, as_str, asm, asm_const, @@ -334,11 +339,14 @@ symbols! { asm_sym, asm_unwind, assert, + assert_eq_macro, assert_inhabited, assert_macro, + assert_ne_macro, assert_receiver_is_total_eq, assert_uninit_valid, assert_zero_valid, + associated_const_equality, associated_consts, associated_type_bounds, associated_type_defaults, @@ -357,7 +365,10 @@ symbols! { augmented_assignments, auto_traits, automatically_derived, + avx, avx512_target_feature, + avx512bw, + avx512f, await_macro, bang, begin_panic, @@ -405,11 +416,14 @@ symbols! { cfg_doctest, cfg_eval, cfg_hide, + cfg_macro, cfg_panic, cfg_sanitize, cfg_target_abi, cfg_target_feature, cfg_target_has_atomic, + cfg_target_has_atomic_equal_alignment, + cfg_target_has_atomic_load_store, cfg_target_thread_local, cfg_target_vendor, cfg_version, @@ -432,21 +446,26 @@ symbols! { coerce_unsized, cold, column, + column_macro, compare_and_swap, compare_exchange, compare_exchange_weak, compile_error, + compile_error_macro, + compiler, compiler_builtins, compiler_fence, concat, concat_bytes, concat_idents, + concat_macro, conservative_impl_trait, console, const_allocate, const_async_blocks, const_compare_raw_pointers, const_constructor, + const_deallocate, const_eval_limit, const_eval_select, const_eval_select_ct, @@ -520,10 +539,13 @@ symbols! { custom_inner_attributes, custom_test_frameworks, d, + dbg_macro, dead_code, dealloc, debug, + debug_assert_eq_macro, debug_assert_macro, + debug_assert_ne_macro, debug_assertions, debug_struct, debug_trait_builder, @@ -567,6 +589,7 @@ symbols! { doc_spotlight, doctest, document_private_items, + dotdot: "..", dotdot_in_tuple_patterns, dotdoteq_in_patterns, dreg, @@ -580,6 +603,7 @@ symbols! { dylib, dyn_metadata, dyn_trait, + e, edition_macro_pats, edition_panic, eh_catch_typeinfo, @@ -592,7 +616,11 @@ symbols! { enable, enclosing_scope, encode, + end, env, + env_macro, + eprint_macro, + eprintln_macro, eq, ermsb_target_feature, exact_div, @@ -640,9 +668,11 @@ symbols! { field, field_init_shorthand, file, + file_macro, fill, finish, flags, + float, float_to_int_unchecked, floorf32, floorf64, @@ -662,8 +692,10 @@ symbols! { format, format_args, format_args_capture, + format_args_macro, format_args_nl, format_macro, + fp, freeze, freg, frem_fast, @@ -728,7 +760,10 @@ symbols! { in_band_lifetimes, include, include_bytes, + include_bytes_macro, + include_macro, include_str, + include_str_macro, inclusive_range_syntax, index, index_mut, @@ -741,6 +776,8 @@ symbols! { inline_const_pat, inout, instruction_set, + integer_: "integer", + integral, intel, into_future, into_iter, @@ -776,6 +813,7 @@ symbols! { lifetime, likely, line, + line_macro, link, link_args, link_cfg, @@ -784,9 +822,9 @@ symbols! { link_ordinal, link_section, linkage, + linker, lint_reasons, literal, - llvm_asm, load, loaded_from_disk, local, @@ -819,6 +857,7 @@ symbols! { masked, match_beginning_vert, match_default_bindings, + matches_macro, maxnumf32, maxnumf64, may_dangle, @@ -837,6 +876,7 @@ symbols! { mem_zeroed, member_constraints, memory, + memtag, message, meta, metadata_type, @@ -855,6 +895,7 @@ symbols! { modifiers, module, module_path, + module_path_macro, more_qualified_paths, more_struct_aliases, movbe_target_feature, @@ -870,6 +911,7 @@ symbols! { naked, naked_functions, name, + names, native_link_modifiers, native_link_modifiers_as_needed, native_link_modifiers_bundle, @@ -884,6 +926,7 @@ symbols! { neg, negate_unsigned, negative_impls, + neon, never, never_type, never_type_fallback, @@ -938,6 +981,7 @@ symbols! { optin_builtin_traits, option, option_env, + option_env_macro, options, or, or_patterns, @@ -958,6 +1002,7 @@ symbols! { panic_implementation, panic_info, panic_location, + panic_no_unwind, panic_runtime, panic_str, panic_unwind, @@ -1001,6 +1046,8 @@ symbols! { prelude_import, preserves_flags, primitive, + print_macro, + println_macro, proc_dash_macro: "proc-macro", proc_macro, proc_macro_attribute, @@ -1075,6 +1122,7 @@ symbols! { repr_packed, repr_simd, repr_transparent, + reserved_r9: "reserved-r9", residual, result, rhs, @@ -1133,9 +1181,11 @@ symbols! { rustc_layout_scalar_valid_range_end, rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, + rustc_lint_query_instability, rustc_macro_transparency, rustc_main, rustc_mir, + rustc_must_implement_one_of, rustc_nonnull_optimization_guaranteed, rustc_object_lifetime_default, rustc_on_unimplemented, @@ -1143,6 +1193,7 @@ symbols! { rustc_paren_sugar, rustc_partition_codegened, rustc_partition_reused, + rustc_pass_by_value, rustc_peek, rustc_peek_definite_init, rustc_peek_liveness, @@ -1186,6 +1237,7 @@ symbols! { simd, simd_add, simd_and, + simd_as, simd_bitmask, simd_cast, simd_ceil, @@ -1267,6 +1319,7 @@ symbols! { sqrtf64, sreg, sreg_low16, + sse, sse4a_target_feature, stable, staged_api, @@ -1288,6 +1341,7 @@ symbols! { str, str_alloc, stringify, + stringify_macro, struct_field_attributes, struct_inherit, struct_variant, @@ -1331,6 +1385,10 @@ symbols! { then_with, thread, thread_local, + thread_local_macro, + thumb2, + thumb_mode: "thumb-mode", + todo_macro, tool_attributes, tool_lints, trace_macros, @@ -1381,6 +1439,7 @@ symbols! { underscore_imports, underscore_lifetimes, uniform_paths, + unimplemented_macro, unit, universal_impl_trait, unix, @@ -1399,6 +1458,7 @@ symbols! { unsafe_block_in_unsafe_fn, unsafe_cell, unsafe_no_drop_flag, + unsafe_pin_internals, unsize, unsized_fn_params, unsized_locals, @@ -1414,6 +1474,7 @@ symbols! { use_extern_macros, use_nested_groups, used, + used_with_arg, usize, v1, va_arg, @@ -1422,10 +1483,13 @@ symbols! { va_list, va_start, val, + values, var, variant_count, vec, + vec_macro, version, + vfp2, vis, visible_private_types, volatile, @@ -1444,12 +1508,15 @@ symbols! { width, windows, windows_subsystem, + with_negative_coherence, wrapping_add, wrapping_mul, wrapping_sub, wreg, write_bytes, + write_macro, write_str, + writeln_macro, x87_reg, xer, xmm_reg, @@ -1711,8 +1778,8 @@ impl Encodable for Symbol { impl Decodable for Symbol { #[inline] - fn decode(d: &mut D) -> Result { - Ok(Symbol::intern(&d.read_str()?)) + fn decode(d: &mut D) -> Symbol { + Symbol::intern(&d.read_str()) } } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index eebf618a5d..cec1d4bc15 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -107,35 +107,35 @@ fn get_symbol_hash<'tcx>( tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); // Include the main item-type. Note that, in this case, the - // assertions about `definitely_needs_subst` may not hold, but this item-type + // assertions about `needs_subst` may not hold, but this item-type // ought to be the same for every reference anyway. - assert!(!item_type.has_erasable_regions(tcx)); + assert!(!item_type.has_erasable_regions()); hcx.while_hashing_spans(false, |hcx| { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { item_type.hash_stable(hcx, &mut hasher); - }); - }); - // If this is a function, we hash the signature as well. - // This is not *strictly* needed, but it may help in some - // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::FnDef(..) = item_type.kind() { - item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher); - } + // If this is a function, we hash the signature as well. + // This is not *strictly* needed, but it may help in some + // situations, see the `run-make/a-b-a-linker-guard` test. + if let ty::FnDef(..) = item_type.kind() { + item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher); + } - // also include any type parameters (for generic items) - substs.hash_stable(&mut hcx, &mut hasher); + // also include any type parameters (for generic items) + substs.hash_stable(hcx, &mut hasher); - if let Some(instantiating_crate) = instantiating_crate { - tcx.def_path_hash(instantiating_crate.as_def_id()) - .stable_crate_id() - .hash_stable(&mut hcx, &mut hasher); - } + if let Some(instantiating_crate) = instantiating_crate { + tcx.def_path_hash(instantiating_crate.as_def_id()) + .stable_crate_id() + .hash_stable(hcx, &mut hasher); + } - // We want to avoid accidental collision between different types of instances. - // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original - // instances without this. - discriminant(&instance.def).hash_stable(&mut hcx, &mut hasher); + // We want to avoid accidental collision between different types of instances. + // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original + // instances without this. + discriminant(&instance.def).hash_stable(hcx, &mut hasher); + }); + }); }); // 64 bits should be enough to avoid collisions. @@ -243,10 +243,10 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { Ok(self) } - fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result { + fn print_const(self, ct: ty::Const<'tcx>) -> Result { // only print integers - if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int { .. })) = ct.val { - if ct.ty.is_integral() { + if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int { .. })) = ct.val() { + if ct.ty().is_integral() { return self.pretty_print_const(ct, true); } } diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 1f38240ee4..f4d1f41902 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -91,6 +91,7 @@ #![feature(never_type)] #![feature(nll)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_middle; @@ -173,8 +174,7 @@ fn compute_symbol_name<'tcx>( let stable_crate_id = tcx.sess.local_stable_crate_id(); return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); } - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - matches!(tcx.hir().get(hir_id), Node::ForeignItem(_)) + matches!(tcx.hir().get_by_def_id(def_id), Node::ForeignItem(_)) } else { tcx.is_foreign_item(def_id) }; diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index c2519adcbe..c21c3d3ac3 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -116,7 +116,7 @@ struct SymbolMangler<'tcx> { /// The values are start positions in `out`, in bytes. paths: FxHashMap<(DefId, &'tcx [GenericArg<'tcx>]), usize>, types: FxHashMap, usize>, - consts: FxHashMap<&'tcx ty::Const<'tcx>, usize>, + consts: FxHashMap, usize>, } impl<'tcx> SymbolMangler<'tcx> { @@ -317,9 +317,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // Encode impl generic params if the substitutions contain parameters (implying // polymorphization is enabled) and this isn't an inherent impl. - if impl_trait_ref.is_some() - && substs.iter().any(|a| a.definitely_has_param_types_or_consts(self.tcx)) - { + if impl_trait_ref.is_some() && substs.iter().any(|a| a.has_param_types_or_consts()) { self = self.path_generic_args( |this| { this.path_append_ns( @@ -422,7 +420,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { hir::Mutability::Not => "R", hir::Mutability::Mut => "Q", }); - if *r != ty::ReErased { + if !r.is_erased() { self = r.print(self)?; } self = ty.print(self)?; @@ -558,10 +556,13 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { cx = cx.print_def_path(trait_ref.def_id, trait_ref.substs)?; } ty::ExistentialPredicate::Projection(projection) => { - let name = cx.tcx.associated_item(projection.item_def_id).ident; + let name = cx.tcx.associated_item(projection.item_def_id).name; cx.push("p"); cx.push_ident(name.as_str()); - cx = projection.ty.print(cx)?; + cx = match projection.term { + ty::Term::Ty(ty) => ty.print(cx), + ty::Term::Const(c) => c.print(cx), + }?; } ty::ExistentialPredicate::AutoTrait(def_id) => { cx = cx.print_def_path(*def_id, &[])?; @@ -575,10 +576,10 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { Ok(self) } - fn print_const(mut self, ct: &'tcx ty::Const<'tcx>) -> Result { + fn print_const(mut self, ct: ty::Const<'tcx>) -> Result { // We only mangle a typed value if the const can be evaluated. let ct = ct.eval(self.tcx, ty::ParamEnv::reveal_all()); - match ct.val { + match ct.val() { ty::ConstKind::Value(_) => {} // Placeholders (should be demangled as `_`). @@ -602,14 +603,14 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { } let start = self.out.len(); - match ct.ty.kind() { + match ct.ty().kind() { ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => { - self = ct.ty.print(self)?; + self = ct.ty().print(self)?; - let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty); + let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty()); // Negative integer values are mangled using `n` as a "sign prefix". - if let ty::Int(ity) = ct.ty.kind() { + if let ty::Int(ity) = ct.ty().kind() { let val = Integer::from_int_ty(&self.tcx, *ity).size().sign_extend(bits) as i128; if val < 0 { @@ -626,7 +627,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // handle `&str` and include both `&` ("R") and `str` ("e") prefixes. ty::Ref(_, ty, hir::Mutability::Not) if *ty == self.tcx.types.str_ => { self.push("R"); - match ct.val { + match ct.val() { ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { // NOTE(eddyb) the following comment was kept from `ty::print::pretty`: // The `inspect` here is okay since we checked the bounds, and there are no @@ -670,7 +671,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { Ok(this) }; - match *ct.ty.kind() { + match *ct.ty().kind() { ty::Array(..) => { self.push("A"); self = print_field_list(self)?; @@ -720,7 +721,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { } _ => { - bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty, ct); + bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty(), ct); } } @@ -771,9 +772,9 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { disambiguated_data: &DisambiguatedDefPathData, ) -> Result { let ns = match disambiguated_data.data { - // FIXME: It shouldn't be necessary to add anything for extern block segments, - // but we add 't' for backward compatibility. - DefPathData::ForeignMod => 't', + // Extern block segments can be skipped, names from extern blocks + // are effectively living in their parent modules. + DefPathData::ForeignMod => return print_prefix(self), // Uppercase categories are more stable than lowercase ones. DefPathData::TypeNs(_) => 't', @@ -810,7 +811,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { ) -> Result { // Don't print any regions if they're all erased. let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, + GenericArgKind::Lifetime(r) => !r.is_erased(), _ => false, }); let args = args.iter().cloned().filter(|arg| match arg.unpack() { diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 735b7e76e3..34324a5829 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -1,6 +1,7 @@ use crate::abi::{self, Abi, Align, FieldsShape, Size}; use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout}; use crate::spec::{self, HasTargetSpec}; +use rustc_span::Symbol; use std::fmt; mod aarch64; @@ -73,6 +74,7 @@ mod attr_impl { // or not to actually emit the attribute. It can also be controlled // with the `-Zmutable-noalias` debugging option. const NoAliasMutRef = 1 << 6; + const NoUndef = 1 << 7; } } } @@ -494,7 +496,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> { // 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 + .set(ArgAttribute::NoAlias) + .set(ArgAttribute::NoCapture) + .set(ArgAttribute::NonNull) + .set(ArgAttribute::NoUndef); 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. @@ -623,10 +629,10 @@ pub struct FnAbi<'a, Ty> { } /// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI. -#[derive(Clone, Debug, HashStable_Generic)] +#[derive(Copy, Clone, Debug, HashStable_Generic)] pub enum AdjustForForeignAbiError { /// Target architecture doesn't support "foreign" (i.e. non-Rust) ABIs. - Unsupported { arch: String, abi: spec::abi::Abi }, + Unsupported { arch: Symbol, abi: spec::abi::Abi }, } impl fmt::Display for AdjustForForeignAbiError { @@ -658,22 +664,24 @@ impl<'a, Ty> FnAbi<'a, Ty> { match &cx.target_spec().arch[..] { "x86" => { - let flavor = if abi == spec::abi::Abi::Fastcall { + let flavor = if let spec::abi::Abi::Fastcall { .. } = abi { x86::Flavor::Fastcall } else { x86::Flavor::General }; x86::compute_abi_info(cx, self, flavor); } - "x86_64" => { - if abi == spec::abi::Abi::SysV64 { - x86_64::compute_abi_info(cx, self); - } else if abi == spec::abi::Abi::Win64 || cx.target_spec().is_like_windows { - x86_win64::compute_abi_info(self); - } else { - x86_64::compute_abi_info(cx, self); + "x86_64" => match abi { + spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), + spec::abi::Abi::Win64 { .. } => x86_win64::compute_abi_info(self), + _ => { + if cx.target_spec().is_like_windows { + x86_win64::compute_abi_info(self) + } else { + x86_64::compute_abi_info(cx, self) + } } - } + }, "aarch64" => aarch64::compute_abi_info(cx, self), "amdgpu" => amdgpu::compute_abi_info(cx, self), "arm" => arm::compute_abi_info(cx, self), @@ -701,7 +709,10 @@ impl<'a, Ty> FnAbi<'a, Ty> { "asmjs" => wasm::compute_c_abi_info(cx, self), "bpf" => bpf::compute_abi_info(self), arch => { - return Err(AdjustForForeignAbiError::Unsupported { arch: arch.to_string(), abi }); + return Err(AdjustForForeignAbiError::Unsupported { + arch: Symbol::intern(arch), + abi, + }); } } diff --git a/compiler/rustc_target/src/abi/call/s390x.rs b/compiler/rustc_target/src/abi/call/s390x.rs index 38aaee64a4..13706e8c21 100644 --- a/compiler/rustc_target/src/abi/call/s390x.rs +++ b/compiler/rustc_target/src/abi/call/s390x.rs @@ -2,7 +2,7 @@ // for a pre-z13 machine or using -mno-vx. use crate::abi::call::{ArgAbi, FnAbi, Reg}; -use crate::abi::{self, HasDataLayout, TyAbiInterface, TyAndLayout}; +use crate::abi::{HasDataLayout, TyAbiInterface}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { if !ret.layout.is_aggregate() && ret.layout.size.bits() <= 64 { @@ -12,24 +12,6 @@ fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { } } -fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool -where - Ty: TyAbiInterface<'a, C>, - C: HasDataLayout, -{ - match layout.abi { - abi::Abi::Scalar(scalar) => scalar.value.is_float(), - abi::Abi::Aggregate { .. } => { - if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 { - is_single_fp_element(cx, layout.field(cx, 0)) - } else { - false - } - } - _ => false, - } -} - fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, @@ -40,7 +22,7 @@ where return; } - if is_single_fp_element(cx, arg.layout) { + if arg.layout.is_single_fp_element(cx) { match arg.layout.size.bytes() { 4 => arg.cast_to(Reg::f32()), 8 => arg.cast_to(Reg::f64()), diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index 28064d85bf..d169087dfb 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -1,5 +1,5 @@ use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind}; -use crate::abi::{self, HasDataLayout, TyAbiInterface, TyAndLayout}; +use crate::abi::{HasDataLayout, TyAbiInterface}; use crate::spec::HasTargetSpec; #[derive(PartialEq)] @@ -8,24 +8,6 @@ pub enum Flavor { Fastcall, } -fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool -where - Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout, -{ - match layout.abi { - abi::Abi::Scalar(scalar) => scalar.value.is_float(), - abi::Abi::Aggregate { .. } => { - if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 { - is_single_fp_element(cx, layout.field(cx, 0)) - } else { - false - } - } - _ => false, - } -} - pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor) where Ty: TyAbiInterface<'a, C> + Copy, @@ -44,7 +26,7 @@ where if t.abi_return_struct_as_int { // According to Clang, everyone but MSVC returns single-element // float aggregates directly in a floating-point register. - if !t.is_like_msvc && is_single_fp_element(cx, fn_abi.ret.layout) { + if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) { match fn_abi.ret.layout.size.bytes() { 4 => fn_abi.ret.cast_to(Reg::f32()), 8 => fn_abi.ret.cast_to(Reg::f64()), diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index a57ad8f2bb..7f1fd28b30 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -1276,6 +1276,24 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { { Ty::ty_and_layout_pointee_info_at(self, cx, offset) } + + pub fn is_single_fp_element(self, cx: &C) -> bool + where + Ty: TyAbiInterface<'a, C>, + C: HasDataLayout, + { + match self.abi { + Abi::Scalar(scalar) => scalar.value.is_float(), + Abi::Aggregate { .. } => { + if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 { + self.field(cx, 0).is_single_fp_element(cx) + } else { + false + } + } + _ => false, + } + } } impl<'a, Ty> TyAndLayout<'a, Ty> { diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 4bf909ce46..d184ad4e78 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,6 +1,8 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::Target; +use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; use std::fmt; def_reg_class! { @@ -58,11 +60,11 @@ impl AArch64InlineAsmRegClass { pub fn supported_types( self, _arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg => types! { _: I8, I16, I32, I64, F32, F64; }, Self::vreg | Self::vreg_low16 => types! { - "fp": I8, I16, I32, I64, F32, F64, + fp: I8, I16, I32, I64, F32, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2), VecF64(1), VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); }, @@ -73,8 +75,9 @@ impl AArch64InlineAsmRegClass { pub fn reserved_x18( _arch: InlineAsmArch, - _has_feature: impl FnMut(&str) -> bool, + _target_features: &FxHashSet, target: &Target, + _is_clobber: bool, ) -> Result<(), &'static str> { if target.os == "android" || target.is_like_fuchsia diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index b03594b315..b2d5bb3736 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,6 +1,8 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::Target; +use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; +use rustc_span::{sym, Symbol}; use std::fmt; def_reg_class! { @@ -44,31 +46,34 @@ impl ArmInlineAsmRegClass { pub fn supported_types( self, _arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg => types! { _: I8, I16, I32, F32; }, - Self::sreg | Self::sreg_low16 => types! { "vfp2": I32, F32; }, + Self::sreg | Self::sreg_low16 => types! { vfp2: I32, F32; }, Self::dreg | Self::dreg_low16 | Self::dreg_low8 => types! { - "vfp2": I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2); + vfp2: I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2); }, Self::qreg | Self::qreg_low8 | Self::qreg_low4 => types! { - "neon": VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4); + neon: VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4); }, } } } // This uses the same logic as useR7AsFramePointer in LLVM -fn frame_pointer_is_r7(mut has_feature: impl FnMut(&str) -> bool, target: &Target) -> bool { - target.is_like_osx || (!target.is_like_windows && has_feature("thumb-mode")) +fn frame_pointer_is_r7(target_features: &FxHashSet, target: &Target) -> bool { + target.is_like_osx || (!target.is_like_windows && target_features.contains(&sym::thumb_mode)) } fn frame_pointer_r11( - _arch: InlineAsmArch, - has_feature: impl FnMut(&str) -> bool, + arch: InlineAsmArch, + target_features: &FxHashSet, target: &Target, + is_clobber: bool, ) -> Result<(), &'static str> { - if !frame_pointer_is_r7(has_feature, target) { + not_thumb1(arch, target_features, target, is_clobber)?; + + if !frame_pointer_is_r7(target_features, target) { Err("the frame pointer (r11) cannot be used as an operand for inline asm") } else { Ok(()) @@ -77,10 +82,11 @@ fn frame_pointer_r11( fn frame_pointer_r7( _arch: InlineAsmArch, - has_feature: impl FnMut(&str) -> bool, + target_features: &FxHashSet, target: &Target, + _is_clobber: bool, ) -> Result<(), &'static str> { - if frame_pointer_is_r7(has_feature, target) { + if frame_pointer_is_r7(target_features, target) { Err("the frame pointer (r7) cannot be used as an operand for inline asm") } else { Ok(()) @@ -89,11 +95,15 @@ fn frame_pointer_r7( fn not_thumb1( _arch: InlineAsmArch, - mut has_feature: impl FnMut(&str) -> bool, + target_features: &FxHashSet, _target: &Target, + is_clobber: bool, ) -> Result<(), &'static str> { - if has_feature("thumb-mode") && !has_feature("thumb2") { - Err("high registers (r8+) cannot be used in Thumb-1 code") + if !is_clobber + && target_features.contains(&sym::thumb_mode) + && !target_features.contains(&sym::thumb2) + { + Err("high registers (r8+) can only be used as clobbers in Thumb-1 code") } else { Ok(()) } @@ -101,14 +111,15 @@ fn not_thumb1( fn reserved_r9( arch: InlineAsmArch, - mut has_feature: impl FnMut(&str) -> bool, + target_features: &FxHashSet, target: &Target, + is_clobber: bool, ) -> Result<(), &'static str> { - not_thumb1(arch, &mut has_feature, target)?; + not_thumb1(arch, target_features, target, is_clobber)?; // We detect this using the reserved-r9 feature instead of using the target // because the relocation model can be changed with compiler options. - if has_feature("reserved-r9") { + if target_features.contains(&sym::reserved_r9) { Err("the RWPI static base register (r9) cannot be used as an operand for inline asm") } else { Ok(()) diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs index 82a4f8bacb..9a96a61f5b 100644 --- a/compiler/rustc_target/src/asm/avr.rs +++ b/compiler/rustc_target/src/asm/avr.rs @@ -1,5 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; use std::fmt; def_reg_class! { @@ -39,7 +40,7 @@ impl AvrInlineAsmRegClass { pub fn supported_types( self, _arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg => types! { _: I8; }, Self::reg_upper => types! { _: I8; }, diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs index ecb6bdc95c..b4d982f383 100644 --- a/compiler/rustc_target/src/asm/bpf.rs +++ b/compiler/rustc_target/src/asm/bpf.rs @@ -1,5 +1,7 @@ use super::{InlineAsmArch, InlineAsmType, Target}; +use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; +use rustc_span::{sym, Symbol}; use std::fmt; def_reg_class! { @@ -33,20 +35,21 @@ impl BpfInlineAsmRegClass { pub fn supported_types( self, _arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg => types! { _: I8, I16, I32, I64; }, - Self::wreg => types! { "alu32": I8, I16, I32; }, + Self::wreg => types! { alu32: I8, I16, I32; }, } } } fn only_alu32( _arch: InlineAsmArch, - mut has_feature: impl FnMut(&str) -> bool, + target_features: &FxHashSet, _target: &Target, + _is_clobber: bool, ) -> Result<(), &'static str> { - if !has_feature("alu32") { + if !target_features.contains(&sym::alu32) { Err("register can't be used without the `alu32` target feature") } else { Ok(()) diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs index 74afddb69d..d20270ac9e 100644 --- a/compiler/rustc_target/src/asm/hexagon.rs +++ b/compiler/rustc_target/src/asm/hexagon.rs @@ -1,5 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; use std::fmt; def_reg_class! { @@ -32,7 +33,7 @@ impl HexagonInlineAsmRegClass { pub fn supported_types( self, _arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg => types! { _: I8, I16, I32, F32; }, } diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs index b19489aa43..b1e8737b52 100644 --- a/compiler/rustc_target/src/asm/mips.rs +++ b/compiler/rustc_target/src/asm/mips.rs @@ -1,5 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; use std::fmt; def_reg_class! { @@ -33,7 +34,7 @@ impl MipsInlineAsmRegClass { pub fn supported_types( self, arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match (self, arch) { (Self::reg, InlineAsmArch::Mips64) => types! { _: I8, I16, I32, I64, F32, F64; }, (Self::reg, _) => types! { _: I8, I16, I32, F32; }, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 9128e54682..fd95b0338a 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -81,14 +81,15 @@ macro_rules! def_regs { pub fn parse( _arch: super::InlineAsmArch, - mut _has_feature: impl FnMut(&str) -> bool, + _target_features: &rustc_data_structures::fx::FxHashSet, _target: &crate::spec::Target, + _is_clobber: bool, name: &str, ) -> Result { match name { $( $($alias)|* | $reg_name => { - $($filter(_arch, &mut _has_feature, _target)?;)? + $($filter(_arch, _target_features, _target, _is_clobber)?;)? Ok(Self::$reg) } )* @@ -102,7 +103,7 @@ macro_rules! def_regs { pub(super) fn fill_reg_map( _arch: super::InlineAsmArch, - mut _has_feature: impl FnMut(&str) -> bool, + _target_features: &rustc_data_structures::fx::FxHashSet, _target: &crate::spec::Target, _map: &mut rustc_data_structures::fx::FxHashMap< super::InlineAsmRegClass, @@ -112,7 +113,7 @@ macro_rules! def_regs { #[allow(unused_imports)] use super::{InlineAsmReg, InlineAsmRegClass}; $( - if $($filter(_arch, &mut _has_feature, _target).is_ok() &&)? true { + if $($filter(_arch, _target_features, _target, false).is_ok() &&)? true { if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { set.insert(InlineAsmReg::$arch($arch_reg::$reg)); } @@ -130,7 +131,7 @@ macro_rules! def_regs { macro_rules! types { ( $(_ : $($ty:expr),+;)? - $($feature:literal: $($ty2:expr),+;)* + $($feature:ident: $($ty2:expr),+;)* ) => { { use super::InlineAsmType::*; @@ -139,7 +140,7 @@ macro_rules! types { ($ty, None), )*)? $($( - ($ty2, Some($feature)), + ($ty2, Some(rustc_span::sym::$feature)), )*)* ] } @@ -152,6 +153,7 @@ mod avr; mod bpf; mod hexagon; mod mips; +mod msp430; mod nvptx; mod powerpc; mod riscv; @@ -166,6 +168,7 @@ pub use avr::{AvrInlineAsmReg, AvrInlineAsmRegClass}; pub use bpf::{BpfInlineAsmReg, BpfInlineAsmRegClass}; pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass}; +pub use msp430::{Msp430InlineAsmReg, Msp430InlineAsmRegClass}; pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; pub use powerpc::{PowerPCInlineAsmReg, PowerPCInlineAsmRegClass}; pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; @@ -194,6 +197,7 @@ pub enum InlineAsmArch { Wasm64, Bpf, Avr, + Msp430, } impl FromStr for InlineAsmArch { @@ -219,6 +223,7 @@ impl FromStr for InlineAsmArch { "wasm64" => Ok(Self::Wasm64), "bpf" => Ok(Self::Bpf), "avr" => Ok(Self::Avr), + "msp430" => Ok(Self::Msp430), _ => Err(()), } } @@ -250,6 +255,7 @@ pub enum InlineAsmReg { Wasm(WasmInlineAsmReg), Bpf(BpfInlineAsmReg), Avr(AvrInlineAsmReg), + Msp430(Msp430InlineAsmReg), // Placeholder for invalid register constraints for the current target Err, } @@ -267,6 +273,7 @@ impl InlineAsmReg { Self::S390x(r) => r.name(), Self::Bpf(r) => r.name(), Self::Avr(r) => r.name(), + Self::Msp430(r) => r.name(), Self::Err => "", } } @@ -283,14 +290,16 @@ impl InlineAsmReg { Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()), Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()), Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()), + Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()), Self::Err => InlineAsmRegClass::Err, } } pub fn parse( arch: InlineAsmArch, - has_feature: impl FnMut(&str) -> bool, + target_features: &FxHashSet, target: &Target, + is_clobber: bool, name: Symbol, ) -> Result { // FIXME: use direct symbol comparison for register names @@ -298,44 +307,79 @@ impl InlineAsmReg { let name = name.as_str(); Ok(match arch { InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - Self::X86(X86InlineAsmReg::parse(arch, has_feature, target, name)?) + Self::X86(X86InlineAsmReg::parse(arch, target_features, target, is_clobber, name)?) } InlineAsmArch::Arm => { - Self::Arm(ArmInlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::AArch64 => { - Self::AArch64(AArch64InlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { - Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::Nvptx64 => { - Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { - Self::PowerPC(PowerPCInlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::Hexagon => { - Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::Mips | InlineAsmArch::Mips64 => { - Self::Mips(MipsInlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::S390x => { - Self::S390x(S390xInlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::SpirV => { - Self::SpirV(SpirVInlineAsmReg::parse(arch, has_feature, target, name)?) - } - InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { - Self::Wasm(WasmInlineAsmReg::parse(arch, has_feature, target, name)?) + Self::Arm(ArmInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?) } + InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmReg::parse( + arch, + target_features, + target, + is_clobber, + name, + )?), + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => Self::RiscV( + RiscVInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?, + ), + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmReg::parse( + arch, + target_features, + target, + is_clobber, + name, + )?), + InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => Self::PowerPC( + PowerPCInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?, + ), + InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmReg::parse( + arch, + target_features, + target, + is_clobber, + name, + )?), + InlineAsmArch::Mips | InlineAsmArch::Mips64 => Self::Mips(MipsInlineAsmReg::parse( + arch, + target_features, + target, + is_clobber, + name, + )?), + InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse( + arch, + target_features, + target, + is_clobber, + name, + )?), + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmReg::parse( + arch, + target_features, + target, + is_clobber, + name, + )?), + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => Self::Wasm(WasmInlineAsmReg::parse( + arch, + target_features, + target, + is_clobber, + name, + )?), InlineAsmArch::Bpf => { - Self::Bpf(BpfInlineAsmReg::parse(arch, has_feature, target, name)?) + Self::Bpf(BpfInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?) } InlineAsmArch::Avr => { - Self::Avr(AvrInlineAsmReg::parse(arch, has_feature, target, name)?) + Self::Avr(AvrInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?) } + InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmReg::parse( + arch, + target_features, + target, + is_clobber, + name, + )?), }) } @@ -358,6 +402,7 @@ impl InlineAsmReg { Self::S390x(r) => r.emit(out, arch, modifier), Self::Bpf(r) => r.emit(out, arch, modifier), Self::Avr(r) => r.emit(out, arch, modifier), + Self::Msp430(r) => r.emit(out, arch, modifier), Self::Err => unreachable!("Use of InlineAsmReg::Err"), } } @@ -374,6 +419,7 @@ impl InlineAsmReg { Self::S390x(_) => cb(self), Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))), Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))), + Self::Msp430(_) => cb(self), Self::Err => unreachable!("Use of InlineAsmReg::Err"), } } @@ -405,6 +451,7 @@ pub enum InlineAsmRegClass { Wasm(WasmInlineAsmRegClass), Bpf(BpfInlineAsmRegClass), Avr(AvrInlineAsmRegClass), + Msp430(Msp430InlineAsmRegClass), // Placeholder for invalid register constraints for the current target Err, } @@ -425,12 +472,13 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.name(), Self::Bpf(r) => r.name(), Self::Avr(r) => r.name(), + Self::Msp430(r) => r.name(), Self::Err => rustc_span::symbol::sym::reg, } } /// Returns a suggested register class to use for this type. This is called - /// after type checking via `supported_types` fails to give a better error + /// when `supported_types` fails to give a better error /// message to the user. pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { match self { @@ -447,6 +495,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm), Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf), Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr), + Self::Msp430(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Msp430), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -476,6 +525,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.suggest_modifier(arch, ty), Self::Bpf(r) => r.suggest_modifier(arch, ty), Self::Avr(r) => r.suggest_modifier(arch, ty), + Self::Msp430(r) => r.suggest_modifier(arch, ty), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -501,6 +551,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.default_modifier(arch), Self::Bpf(r) => r.default_modifier(arch), Self::Avr(r) => r.default_modifier(arch), + Self::Msp430(r) => r.default_modifier(arch), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -510,7 +561,7 @@ impl InlineAsmRegClass { pub fn supported_types( self, arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::X86(r) => r.supported_types(arch), Self::Arm(r) => r.supported_types(arch), @@ -525,6 +576,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.supported_types(arch), Self::Bpf(r) => r.supported_types(arch), Self::Avr(r) => r.supported_types(arch), + Self::Msp430(r) => r.supported_types(arch), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -554,6 +606,7 @@ impl InlineAsmRegClass { } InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(arch, name)?), InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(arch, name)?), }) } @@ -574,6 +627,7 @@ impl InlineAsmRegClass { Self::Wasm(r) => r.valid_modifiers(arch), Self::Bpf(r) => r.valid_modifiers(arch), Self::Avr(r) => r.valid_modifiers(arch), + Self::Msp430(r) => r.valid_modifiers(arch), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -695,73 +749,78 @@ impl fmt::Display for InlineAsmType { // falling back to an external assembler. pub fn allocatable_registers( arch: InlineAsmArch, - has_feature: impl FnMut(&str) -> bool, + target_features: &FxHashSet, target: &crate::spec::Target, ) -> FxHashMap> { match arch { InlineAsmArch::X86 | InlineAsmArch::X86_64 => { let mut map = x86::regclass_map(); - x86::fill_reg_map(arch, has_feature, target, &mut map); + x86::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::Arm => { let mut map = arm::regclass_map(); - arm::fill_reg_map(arch, has_feature, target, &mut map); + arm::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::AArch64 => { let mut map = aarch64::regclass_map(); - aarch64::fill_reg_map(arch, has_feature, target, &mut map); + aarch64::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { let mut map = riscv::regclass_map(); - riscv::fill_reg_map(arch, has_feature, target, &mut map); + riscv::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::Nvptx64 => { let mut map = nvptx::regclass_map(); - nvptx::fill_reg_map(arch, has_feature, target, &mut map); + nvptx::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { let mut map = powerpc::regclass_map(); - powerpc::fill_reg_map(arch, has_feature, target, &mut map); + powerpc::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::Hexagon => { let mut map = hexagon::regclass_map(); - hexagon::fill_reg_map(arch, has_feature, target, &mut map); + hexagon::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::Mips | InlineAsmArch::Mips64 => { let mut map = mips::regclass_map(); - mips::fill_reg_map(arch, has_feature, target, &mut map); + mips::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::S390x => { let mut map = s390x::regclass_map(); - s390x::fill_reg_map(arch, has_feature, target, &mut map); + s390x::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::SpirV => { let mut map = spirv::regclass_map(); - spirv::fill_reg_map(arch, has_feature, target, &mut map); + spirv::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { let mut map = wasm::regclass_map(); - wasm::fill_reg_map(arch, has_feature, target, &mut map); + wasm::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::Bpf => { let mut map = bpf::regclass_map(); - bpf::fill_reg_map(arch, has_feature, target, &mut map); + bpf::fill_reg_map(arch, target_features, target, &mut map); map } InlineAsmArch::Avr => { let mut map = avr::regclass_map(); - avr::fill_reg_map(arch, has_feature, target, &mut map); + avr::fill_reg_map(arch, target_features, target, &mut map); + map + } + InlineAsmArch::Msp430 => { + let mut map = msp430::regclass_map(); + msp430::fill_reg_map(arch, target_features, target, &mut map); map } } @@ -794,7 +853,7 @@ impl InlineAsmClobberAbi { /// clobber ABIs for the target. pub fn parse( arch: InlineAsmArch, - has_feature: impl FnMut(&str) -> bool, + target_features: &FxHashSet, target: &Target, name: Symbol, ) -> Result { @@ -819,7 +878,7 @@ impl InlineAsmClobberAbi { }, InlineAsmArch::AArch64 => match name { "C" | "system" | "efiapi" => { - Ok(if aarch64::reserved_x18(arch, has_feature, target).is_err() { + Ok(if aarch64::reserved_x18(arch, target_features, target, true).is_err() { InlineAsmClobberAbi::AArch64NoX18 } else { InlineAsmClobberAbi::AArch64 diff --git a/compiler/rustc_target/src/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs new file mode 100644 index 0000000000..a27d6390a7 --- /dev/null +++ b/compiler/rustc_target/src/asm/msp430.rs @@ -0,0 +1,81 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; + +def_reg_class! { + Msp430 Msp430InlineAsmRegClass { + reg, + } +} + +impl Msp430InlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option)] { + match (self, arch) { + (Self::reg, _) => types! { _: I8, I16; }, + } + } +} + +// The reserved registers are taken from: +// https://github.com/llvm/llvm-project/blob/36cb29cbbe1b22dcd298ad65e1fabe899b7d7249/llvm/lib/Target/MSP430/MSP430RegisterInfo.cpp#L73. +def_regs! { + Msp430 Msp430InlineAsmReg Msp430InlineAsmRegClass { + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + r10: reg = ["r10"], + r11: reg = ["r11"], + r12: reg = ["r12"], + r13: reg = ["r13"], + r14: reg = ["r14"], + r15: reg = ["r15"], + + #error = ["r0", "pc"] => + "the program counter cannot be used as an operand for inline asm", + #error = ["r1", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r2", "sr"] => + "the status register cannot be used as an operand for inline asm", + #error = ["r3", "cg"] => + "the constant generator cannot be used as an operand for inline asm", + #error = ["r4", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + } +} + +impl Msp430InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/compiler/rustc_target/src/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs index 43d16ae0f5..8e1e91e7c5 100644 --- a/compiler/rustc_target/src/asm/nvptx.rs +++ b/compiler/rustc_target/src/asm/nvptx.rs @@ -1,5 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; def_reg_class! { Nvptx NvptxInlineAsmRegClass { @@ -33,7 +34,7 @@ impl NvptxInlineAsmRegClass { pub fn supported_types( self, _arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg16 => types! { _: I8, I16; }, Self::reg32 => types! { _: I8, I16, I32, F32; }, diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index 51a4303689..d3ccb30350 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -1,5 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; use std::fmt; def_reg_class! { @@ -36,7 +37,7 @@ impl PowerPCInlineAsmRegClass { pub fn supported_types( self, arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg | Self::reg_nonzero => { if arch == InlineAsmArch::PowerPC { diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index 314bd01de1..e145ba8a16 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -1,6 +1,8 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::Target; +use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; +use rustc_span::{sym, Symbol}; use std::fmt; def_reg_class! { @@ -35,7 +37,7 @@ impl RiscVInlineAsmRegClass { pub fn supported_types( self, arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg => { if arch == InlineAsmArch::RiscV64 { @@ -44,7 +46,7 @@ impl RiscVInlineAsmRegClass { types! { _: I8, I16, I32, F32; } } } - Self::freg => types! { "f": F32; "d": F64; }, + Self::freg => types! { f: F32; d: F64; }, Self::vreg => &[], } } @@ -52,10 +54,11 @@ impl RiscVInlineAsmRegClass { fn not_e( _arch: InlineAsmArch, - mut has_feature: impl FnMut(&str) -> bool, + target_features: &FxHashSet, _target: &Target, + _is_clobber: bool, ) -> Result<(), &'static str> { - if has_feature("e") { + if target_features.contains(&sym::e) { Err("register can't be used with the `e` target feature") } else { Ok(()) diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs index a74873f174..0a50064f58 100644 --- a/compiler/rustc_target/src/asm/s390x.rs +++ b/compiler/rustc_target/src/asm/s390x.rs @@ -1,5 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; use std::fmt; def_reg_class! { @@ -33,7 +34,7 @@ impl S390xInlineAsmRegClass { pub fn supported_types( self, arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match (self, arch) { (Self::reg, _) => types! { _: I8, I16, I32, I64; }, (Self::freg, _) => types! { _: F32, F64; }, diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs index da82749e96..31073da10b 100644 --- a/compiler/rustc_target/src/asm/spirv.rs +++ b/compiler/rustc_target/src/asm/spirv.rs @@ -1,5 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; def_reg_class! { SpirV SpirVInlineAsmRegClass { @@ -31,7 +32,7 @@ impl SpirVInlineAsmRegClass { pub fn supported_types( self, _arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg => { types! { _: I8, I16, I32, I64, F32, F64; } diff --git a/compiler/rustc_target/src/asm/wasm.rs b/compiler/rustc_target/src/asm/wasm.rs index 1b33f8f963..f095b7c6e1 100644 --- a/compiler/rustc_target/src/asm/wasm.rs +++ b/compiler/rustc_target/src/asm/wasm.rs @@ -1,5 +1,6 @@ use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; def_reg_class! { Wasm WasmInlineAsmRegClass { @@ -31,7 +32,7 @@ impl WasmInlineAsmRegClass { pub fn supported_types( self, _arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::local => { types! { _: I8, I16, I32, I64, F32, F64; } diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 5e3828d7d8..a8ee80ec4e 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -1,6 +1,8 @@ use super::{InlineAsmArch, InlineAsmType}; use crate::spec::Target; +use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; use std::fmt; def_reg_class! { @@ -101,7 +103,7 @@ impl X86InlineAsmRegClass { pub fn supported_types( self, arch: InlineAsmArch, - ) -> &'static [(InlineAsmType, Option<&'static str>)] { + ) -> &'static [(InlineAsmType, Option)] { match self { Self::reg | Self::reg_abcd => { if arch == InlineAsmArch::X86_64 { @@ -112,23 +114,23 @@ impl X86InlineAsmRegClass { } Self::reg_byte => types! { _: I8; }, Self::xmm_reg => types! { - "sse": I32, I64, F32, F64, + sse: I32, I64, F32, F64, VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); }, Self::ymm_reg => types! { - "avx": I32, I64, F32, F64, + avx: I32, I64, F32, F64, VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4); }, Self::zmm_reg => types! { - "avx512f": I32, I64, F32, F64, + avx512f: I32, I64, F32, F64, VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4), VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF32(16), VecF64(8); }, Self::kreg => types! { - "avx512f": I8, I16; - "avx512bw": I32, I64; + avx512f: I8, I16; + avx512bw: I32, I64; }, Self::mmx_reg | Self::x87_reg => &[], } @@ -137,8 +139,9 @@ impl X86InlineAsmRegClass { fn x86_64_only( arch: InlineAsmArch, - _has_feature: impl FnMut(&str) -> bool, + _target_features: &FxHashSet, _target: &Target, + _is_clobber: bool, ) -> Result<(), &'static str> { match arch { InlineAsmArch::X86 => Err("register is only available on x86_64"), @@ -149,8 +152,9 @@ fn x86_64_only( fn high_byte( arch: InlineAsmArch, - _has_feature: impl FnMut(&str) -> bool, + _target_features: &FxHashSet, _target: &Target, + _is_clobber: bool, ) -> Result<(), &'static str> { match arch { InlineAsmArch::X86_64 => Err("high byte registers cannot be used as an operand on x86_64"), @@ -160,8 +164,9 @@ fn high_byte( fn rbx_reserved( arch: InlineAsmArch, - _has_feature: impl FnMut(&str) -> bool, + _target_features: &FxHashSet, _target: &Target, + _is_clobber: bool, ) -> Result<(), &'static str> { match arch { InlineAsmArch::X86 => Ok(()), @@ -174,8 +179,9 @@ fn rbx_reserved( fn esi_reserved( arch: InlineAsmArch, - _has_feature: impl FnMut(&str) -> bool, + _target_features: &FxHashSet, _target: &Target, + _is_clobber: bool, ) -> Result<(), &'static str> { match arch { InlineAsmArch::X86 => { diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index 1e9abbbe1e..5692925f63 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -14,7 +14,9 @@ pub fn target() -> Target { // As documented in https://developer.android.com/ndk/guides/cpu-features.html // the neon (ASIMD) and FP must exist on all android aarch64 targets. features: "+neon,+fp-armv8".to_string(), - supported_sanitizers: SanitizerSet::CFI | SanitizerSet::HWADDRESS, + supported_sanitizers: SanitizerSet::CFI + | SanitizerSet::HWADDRESS + | SanitizerSet::MEMTAG, ..super::android_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs index 44beb2f6ad..f8e1e1b02f 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs @@ -3,6 +3,7 @@ use crate::spec::Target; pub fn target() -> Target { let mut base = super::hermit_base::opts(); base.max_atomic_width = Some(128); + base.features = "+strict-align,+neon,+fp-armv8".to_string(); Target { llvm_target: "aarch64-unknown-hermit".to_string(), diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs index 850381f7fb..974a5b84d1 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs @@ -14,6 +14,7 @@ pub fn target() -> Target { | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::MEMORY + | SanitizerSet::MEMTAG | SanitizerSet::THREAD | SanitizerSet::HWADDRESS, ..super::linux_gnu_base::opts() diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_hermitkernel.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_hermitkernel.rs new file mode 100644 index 0000000000..6e9d6c6221 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_hermitkernel.rs @@ -0,0 +1,16 @@ +use crate::spec::Target; + +pub fn target() -> Target { + let mut base = super::hermit_kernel_base::opts(); + base.max_atomic_width = Some(128); + base.abi = "softfloat".to_string(); + base.features = "+strict-align,-neon,-fp-armv8".to_string(); + + Target { + llvm_target: "aarch64-unknown-hermit".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: base, + } +} diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index e3a2226eb9..d9e571c72e 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -13,14 +13,14 @@ pub enum Abi { // churn. The specific values are meaningless. Rust, C { unwind: bool }, - Cdecl, + Cdecl { unwind: bool }, Stdcall { unwind: bool }, - Fastcall, - Vectorcall, + Fastcall { unwind: bool }, + Vectorcall { unwind: bool }, Thiscall { unwind: bool }, - Aapcs, - Win64, - SysV64, + Aapcs { unwind: bool }, + Win64 { unwind: bool }, + SysV64 { unwind: bool }, PtxKernel, Msp430Interrupt, X86Interrupt, @@ -50,16 +50,22 @@ const AbiDatas: &[AbiData] = &[ AbiData { abi: Abi::Rust, name: "Rust" }, AbiData { abi: Abi::C { unwind: false }, name: "C" }, AbiData { abi: Abi::C { unwind: true }, name: "C-unwind" }, - AbiData { abi: Abi::Cdecl, name: "cdecl" }, + AbiData { abi: Abi::Cdecl { unwind: false }, name: "cdecl" }, + AbiData { abi: Abi::Cdecl { unwind: true }, name: "cdecl-unwind" }, AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall" }, AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind" }, - AbiData { abi: Abi::Fastcall, name: "fastcall" }, - AbiData { abi: Abi::Vectorcall, name: "vectorcall" }, + AbiData { abi: Abi::Fastcall { unwind: false }, name: "fastcall" }, + AbiData { abi: Abi::Fastcall { unwind: true }, name: "fastcall-unwind" }, + AbiData { abi: Abi::Vectorcall { unwind: false }, name: "vectorcall" }, + AbiData { abi: Abi::Vectorcall { unwind: true }, name: "vectorcall-unwind" }, AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall" }, AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind" }, - AbiData { abi: Abi::Aapcs, name: "aapcs" }, - AbiData { abi: Abi::Win64, name: "win64" }, - AbiData { abi: Abi::SysV64, name: "sysv64" }, + AbiData { abi: Abi::Aapcs { unwind: false }, name: "aapcs" }, + AbiData { abi: Abi::Aapcs { unwind: true }, name: "aapcs-unwind" }, + AbiData { abi: Abi::Win64 { unwind: false }, name: "win64" }, + AbiData { abi: Abi::Win64 { unwind: true }, name: "win64-unwind" }, + AbiData { abi: Abi::SysV64 { unwind: false }, name: "sysv64" }, + AbiData { abi: Abi::SysV64 { unwind: true }, name: "sysv64-unwind" }, AbiData { abi: Abi::PtxKernel, name: "ptx-kernel" }, AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt" }, AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt" }, @@ -101,32 +107,38 @@ impl Abi { C { unwind: false } => 1, C { unwind: true } => 2, // Platform-specific ABIs - Cdecl => 3, - Stdcall { unwind: false } => 4, - Stdcall { unwind: true } => 5, - Fastcall => 6, - Vectorcall => 7, - Thiscall { unwind: false } => 8, - Thiscall { unwind: true } => 9, - Aapcs => 10, - Win64 => 11, - SysV64 => 12, - PtxKernel => 13, - Msp430Interrupt => 14, - X86Interrupt => 15, - AmdGpuKernel => 16, - EfiApi => 17, - AvrInterrupt => 18, - AvrNonBlockingInterrupt => 19, - CCmseNonSecureCall => 20, - Wasm => 21, + Cdecl { unwind: false } => 3, + Cdecl { unwind: true } => 4, + Stdcall { unwind: false } => 5, + Stdcall { unwind: true } => 6, + Fastcall { unwind: false } => 7, + Fastcall { unwind: true } => 8, + Vectorcall { unwind: false } => 9, + Vectorcall { unwind: true } => 10, + Thiscall { unwind: false } => 11, + Thiscall { unwind: true } => 12, + Aapcs { unwind: false } => 13, + Aapcs { unwind: true } => 14, + Win64 { unwind: false } => 15, + Win64 { unwind: true } => 16, + SysV64 { unwind: false } => 17, + SysV64 { unwind: true } => 18, + PtxKernel => 19, + Msp430Interrupt => 20, + X86Interrupt => 21, + AmdGpuKernel => 22, + EfiApi => 23, + AvrInterrupt => 24, + AvrNonBlockingInterrupt => 25, + CCmseNonSecureCall => 26, + Wasm => 27, // Cross-platform ABIs - System { unwind: false } => 22, - System { unwind: true } => 23, - RustIntrinsic => 24, - RustCall => 25, - PlatformIntrinsic => 26, - Unadjusted => 27, + System { unwind: false } => 28, + System { unwind: true } => 29, + RustIntrinsic => 30, + RustCall => 31, + PlatformIntrinsic => 32, + Unadjusted => 33, }; debug_assert!( AbiDatas diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs index e982b3565b..dc14d260e9 100644 --- a/compiler/rustc_target/src/spec/android_base.rs +++ b/compiler/rustc_target/src/spec/android_base.rs @@ -1,14 +1,8 @@ -use crate::spec::{LinkerFlavor, TargetOptions}; +use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); base.os = "android".to_string(); - // Many of the symbols defined in compiler-rt are also defined in libgcc. - // Android's linker doesn't like that by default. - base.pre_link_args - .entry(LinkerFlavor::Gcc) - .or_default() - .push("-Wl,--allow-multiple-definition".to_string()); base.dwarf_version = Some(2); base.position_independent_executables = true; base.has_thread_local = false; diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs index afe8bbb352..3e3a6ac82a 100644 --- a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs +++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs @@ -36,6 +36,7 @@ pub fn target() -> Target { features: "+vfp2".to_string(), pre_link_args, exe_suffix: ".elf".to_string(), + no_default_libraries: false, ..Default::default() }, } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabi.rs new file mode 100644 index 0000000000..7faa8ed7a8 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_uclibceabi.rs @@ -0,0 +1,23 @@ +use crate::spec::{Target, TargetOptions}; + +// This target is for uclibc Linux on ARMv7 without NEON, +// thumb-mode or hardfloat. + +pub fn target() -> Target { + let base = super::linux_uclibc_base::opts(); + Target { + llvm_target: "armv7-unknown-linux-gnueabi".to_string(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + + options: TargetOptions { + features: "+v7,+thumb2,+soft-float,-neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + mcount: "_mcount".to_string(), + abi: "eabi".to_string(), + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 2cb2661a52..a6c1b344d7 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -17,12 +17,10 @@ pub fn target(target_cpu: String) -> Target { linker: Some("avr-gcc".to_owned()), executables: true, eh_frame_header: false, - pre_link_args: vec![(LinkerFlavor::Gcc, vec![format!("-mmcu={}", target_cpu)])] - .into_iter() - .collect(), - late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])] + pre_link_args: [(LinkerFlavor::Gcc, vec![format!("-mmcu={}", target_cpu)])] .into_iter() .collect(), + late_link_args: [(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])].into_iter().collect(), max_atomic_width: Some(0), atomic_cas: false, ..TargetOptions::default() diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs index 74074cfb5d..174294895b 100644 --- a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs @@ -24,7 +24,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-msvc".to_string(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:32-n8:16:32-a:0:32-S32" + i64:64-f80:128-n8:16:32-a:0:32-S32" .to_string(), arch: "x86".to_string(), options: base, diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs index 05f204c560..e2f65e7a7c 100644 --- a/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs @@ -9,7 +9,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-msvc".to_string(), pointer_width: 32, data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ - i64:64-f80:32-n8:16:32-a:0:32-S32" + i64:64-f80:128-n8:16:32-a:0:32-S32" .to_string(), arch: "x86".to_string(), options: base, diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs index f6e3102f61..9e7973f63a 100644 --- a/compiler/rustc_target/src/spec/l4re_base.rs +++ b/compiler/rustc_target/src/spec/l4re_base.rs @@ -1,25 +1,14 @@ use crate::spec::{LinkerFlavor, PanicStrategy, TargetOptions}; -//use std::process::Command; - -// Use GCC to locate code for crt* libraries from the host, not from L4Re. Note -// that a few files also come from L4Re, for these, the function shouldn't be -// used. This uses GCC for the location of the file, but GCC is required for L4Re anyway. -//fn get_path_or(filename: &str) -> String { -// let child = Command::new("gcc") -// .arg(format!("-print-file-name={}", filename)).output() -// .expect("Failed to execute GCC"); -// String::from_utf8(child.stdout) -// .expect("Couldn't read path from GCC").trim().into() -//} +use std::default::Default; pub fn opts() -> TargetOptions { TargetOptions { os: "l4re".to_string(), env: "uclibc".to_string(), - linker_flavor: LinkerFlavor::Ld, + linker_flavor: LinkerFlavor::L4Bender, executables: true, panic_strategy: PanicStrategy::Abort, - linker: Some("ld".to_string()), + linker: Some("l4-bender".to_string()), linker_is_gnu: false, families: vec!["unix".to_string()], ..Default::default() diff --git a/compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs new file mode 100644 index 0000000000..5991cd8bfa --- /dev/null +++ b/compiler/rustc_target/src/spec/mips64_openwrt_linux_musl.rs @@ -0,0 +1,26 @@ +/// A target tuple for OpenWrt MIPS64 targets +/// +use crate::abi::Endian; +use crate::spec::{Target, TargetOptions}; + +pub fn target() -> Target { + let mut base = super::linux_musl_base::opts(); + base.cpu = "mips64r2".to_string(); + base.features = "+mips64r2,+soft-float".to_string(); + base.max_atomic_width = Some(64); + base.crt_static_default = false; + + Target { + // LLVM doesn't recognize "muslabi64" yet. + llvm_target: "mips64-unknown-linux-musl".to_string(), + 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 { + abi: "abi64".to_string(), + endian: Endian::Big, + mcount: "_mcount".to_string(), + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ca1949b9f7..92678aed5b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -90,6 +90,7 @@ mod windows_uwp_msvc_base; pub enum LinkerFlavor { Em, Gcc, + L4Bender, Ld, Msvc, Lld(LldFlavor), @@ -160,6 +161,7 @@ macro_rules! flavor_mappings { flavor_mappings! { ((LinkerFlavor::Em), "em"), ((LinkerFlavor::Gcc), "gcc"), + ((LinkerFlavor::L4Bender), "l4-bender"), ((LinkerFlavor::Ld), "ld"), ((LinkerFlavor::Msvc), "msvc"), ((LinkerFlavor::PtxLinker), "ptx-linker"), @@ -574,15 +576,15 @@ 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() + [(String::from("kind"), "none".to_json())].into_iter().collect() } StackProbeType::Inline => { - vec![(String::from("kind"), "inline".to_json())].into_iter().collect() + [(String::from("kind"), "inline".to_json())].into_iter().collect() } StackProbeType::Call => { - vec![(String::from("kind"), "call".to_json())].into_iter().collect() + [(String::from("kind"), "call".to_json())].into_iter().collect() } - StackProbeType::InlineOrCall { min_llvm_version_for_inline } => vec![ + StackProbeType::InlineOrCall { min_llvm_version_for_inline } => [ (String::from("kind"), "inline-or-call".to_json()), ( String::from("min-llvm-version-for-inline"), @@ -604,6 +606,7 @@ bitflags::bitflags! { const THREAD = 1 << 3; const HWADDRESS = 1 << 4; const CFI = 1 << 5; + const MEMTAG = 1 << 6; } } @@ -617,6 +620,7 @@ impl SanitizerSet { SanitizerSet::CFI => "cfi", SanitizerSet::LEAK => "leak", SanitizerSet::MEMORY => "memory", + SanitizerSet::MEMTAG => "memtag", SanitizerSet::THREAD => "thread", SanitizerSet::HWADDRESS => "hwaddress", _ => return None, @@ -650,6 +654,7 @@ impl IntoIterator for SanitizerSet { SanitizerSet::CFI, SanitizerSet::LEAK, SanitizerSet::MEMORY, + SanitizerSet::MEMTAG, SanitizerSet::THREAD, SanitizerSet::HWADDRESS, ] @@ -962,6 +967,7 @@ supported_targets! { ("aarch64-unknown-hermit", aarch64_unknown_hermit), ("x86_64-unknown-hermit", x86_64_unknown_hermit), + ("aarch64-unknown-none-hermitkernel", aarch64_unknown_none_hermitkernel), ("x86_64-unknown-none-hermitkernel", x86_64_unknown_none_hermitkernel), ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), @@ -1011,9 +1017,12 @@ supported_targets! { ("armv6k-nintendo-3ds", armv6k_nintendo_3ds), + ("armv7-unknown-linux-uclibceabi", armv7_unknown_linux_uclibceabi), ("armv7-unknown-linux-uclibceabihf", armv7_unknown_linux_uclibceabihf), ("x86_64-unknown-none", x86_64_unknown_none), + + ("mips64-openwrt-linux-musl", mips64_openwrt_linux_musl), } /// Warnings encountered when parsing the target `json`. @@ -1535,11 +1544,13 @@ impl Default for TargetOptions { impl Deref for Target { type Target = TargetOptions; + #[inline] fn deref(&self) -> &Self::Target { &self.options } } impl DerefMut for Target { + #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.options } @@ -1554,15 +1565,15 @@ impl Target { Abi::Stdcall { unwind } } Abi::System { unwind } => Abi::C { unwind }, - Abi::EfiApi if self.arch == "x86_64" => Abi::Win64, + Abi::EfiApi if self.arch == "x86_64" => Abi::Win64 { unwind: false }, Abi::EfiApi => Abi::C { unwind: false }, // See commentary in `is_abi_supported`. Abi::Stdcall { .. } | Abi::Thiscall { .. } if self.arch == "x86" => abi, Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => Abi::C { unwind }, - Abi::Fastcall if self.arch == "x86" => abi, - Abi::Vectorcall if ["x86", "x86_64"].contains(&&self.arch[..]) => abi, - Abi::Fastcall | Abi::Vectorcall => Abi::C { unwind: false }, + Abi::Fastcall { .. } if self.arch == "x86" => abi, + Abi::Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => abi, + Abi::Fastcall { unwind } | Abi::Vectorcall { unwind } => Abi::C { unwind }, abi => abi, } @@ -1579,12 +1590,12 @@ impl Target { | RustCall | PlatformIntrinsic | Unadjusted - | Cdecl + | Cdecl { .. } | EfiApi => true, X86Interrupt => ["x86", "x86_64"].contains(&&self.arch[..]), - Aapcs => "arm" == self.arch, + Aapcs { .. } => "arm" == self.arch, CCmseNonSecureCall => ["arm", "aarch64"].contains(&&self.arch[..]), - Win64 | SysV64 => self.arch == "x86_64", + Win64 { .. } | SysV64 { .. } => self.arch == "x86_64", PtxKernel => self.arch == "nvptx64", Msp430Interrupt => self.arch == "msp430", AmdGpuKernel => self.arch == "amdgcn", @@ -1621,13 +1632,13 @@ impl Target { // > convention is used. // // -- https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions - Stdcall { .. } | Fastcall | Vectorcall if self.is_like_windows => true, + Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } if self.is_like_windows => true, // Outside of Windows we want to only support these calling conventions for the // architectures for which these calling conventions are actually well defined. - Stdcall { .. } | Fastcall if self.arch == "x86" => true, - Vectorcall if ["x86", "x86_64"].contains(&&self.arch[..]) => true, + Stdcall { .. } | Fastcall { .. } if self.arch == "x86" => true, + Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => true, // Return a `None` for other cases so that we know to emit a future compat lint. - Stdcall { .. } | Fastcall | Vectorcall => return None, + Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } => return None, }) } @@ -1875,6 +1886,7 @@ impl Target { Some("cfi") => SanitizerSet::CFI, Some("leak") => SanitizerSet::LEAK, Some("memory") => SanitizerSet::MEMORY, + Some("memtag") => SanitizerSet::MEMTAG, Some("thread") => SanitizerSet::THREAD, Some("hwaddress") => SanitizerSet::HWADDRESS, Some(s) => return Err(format!("unknown sanitizer {}", s)), @@ -2143,8 +2155,8 @@ impl Target { use std::fs; fn load_file(path: &Path) -> Result<(Target, TargetWarnings), String> { - let contents = fs::read(path).map_err(|e| e.to_string())?; - let obj = json::from_reader(&mut &contents[..]).map_err(|e| e.to_string())?; + let contents = fs::read_to_string(path).map_err(|e| e.to_string())?; + let obj = json::from_str(&contents).map_err(|e| e.to_string())?; Target::from_json(obj) } diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index 69a404ec56..8fcdbc146a 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -43,7 +43,8 @@ pub fn target() -> Target { Target { llvm_target: "wasm32-unknown-emscripten".to_string(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-i64:64-f128:64-n32:64-S128-ni:1:10:20".to_string(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-S128-ni:1:10:20" + .to_string(), arch: "wasm32".to_string(), options: opts, } diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs index 134c6803b1..e50cf97409 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -54,7 +54,7 @@ pub fn target() -> Target { Target { llvm_target: "wasm32-unknown-unknown".to_string(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20".to_string(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".to_string(), arch: "wasm32".to_string(), options, } diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 2dab206dc7..a4b81c9a27 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -109,7 +109,7 @@ pub fn target() -> Target { Target { llvm_target: "wasm32-wasi".to_string(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20".to_string(), + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".to_string(), arch: "wasm32".to_string(), options, } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs index 1fbd0bb4ce..64c7c1c5f6 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs @@ -1,9 +1,12 @@ -use crate::spec::Target; +use crate::spec::{PanicStrategy, Target}; pub fn target() -> Target { let mut base = super::l4re_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); + base.crt_static_allows_dylibs = false; + base.dynamic_linking = false; + base.panic_strategy = PanicStrategy::Abort; Target { llvm_target: "x86_64-unknown-l4re-uclibc".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 c2484f2d8f..aefbb39828 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 @@ -7,6 +7,7 @@ pub fn target() -> Target { base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call; + base.static_position_independent_executables = true; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 17e7b48189..0041f59640 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -22,6 +22,7 @@ #![feature(crate_visibility_modifier)] #![feature(control_flow_enum)] #![recursion_limit = "512"] // For rustdoc +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index ea0ac6318b..c93ff0aa6e 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -141,7 +141,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { #[instrument(skip(self), level = "debug")] fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match r { + match *r { // Ignore bound regions and `'static` regions that appear in the // type, we only need to remap regions that reference lifetimes // from the function declaraion. @@ -287,10 +287,10 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { } } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { trace!("checking const {:?}", ct); // Find a const parameter - match ct.val { + match ct.val() { ty::ConstKind::Param(..) => { // Look it up in the substitution list. match self.map.get(&ct.into()).map(|k| k.unpack()) { @@ -311,7 +311,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { ) .emit(); - self.tcx().const_error(ct.ty) + self.tcx().const_error(ct.ty()) } } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 05d2a373dc..5fe7b62f45 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -6,7 +6,7 @@ use super::*; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::InferCtxt; use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::{Region, RegionVid}; +use rustc_middle::ty::{Region, RegionVid, Term}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -437,16 +437,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { for (new_region, old_region) in iter::zip(new_substs.regions(), old_substs.regions()) { - match (new_region, old_region) { + match (*new_region, *old_region) { // If both predicates have an `ReLateBound` (a HRTB) in the // same spot, we do nothing. - ( - ty::RegionKind::ReLateBound(_, _), - ty::RegionKind::ReLateBound(_, _), - ) => {} + (ty::ReLateBound(_, _), ty::ReLateBound(_, _)) => {} - (ty::RegionKind::ReLateBound(_, _), _) - | (_, ty::RegionKind::ReVar(_)) => { + (ty::ReLateBound(_, _), _) | (_, ty::ReVar(_)) => { // One of these is true: // The new predicate has a HRTB in a spot where the old // predicate does not (if they both had a HRTB, the previous @@ -472,8 +468,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // `user_computed_preds`. return false; } - (_, ty::RegionKind::ReLateBound(_, _)) - | (ty::RegionKind::ReVar(_), _) => { + (_, ty::ReLateBound(_, _)) | (ty::ReVar(_), _) => { // This is the opposite situation as the previous arm. // One of these is true: // @@ -606,7 +601,11 @@ impl<'tcx> AutoTraitFinder<'tcx> { } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { - matches!(*p.ty().skip_binder().kind(), ty::Projection(proj) if proj == p.skip_binder().projection_ty) + if let Term::Ty(ty) = p.term().skip_binder() { + matches!(ty.kind(), ty::Projection(proj) if proj == &p.skip_binder().projection_ty) + } else { + false + } } fn evaluate_nested_obligations( @@ -663,7 +662,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // Additionally, we check if we've seen this predicate before, // to avoid rendering duplicate bounds to the user. if self.is_param_no_infer(p.skip_binder().projection_ty.substs) - && !p.ty().skip_binder().has_infer_types() + && !p.term().skip_binder().has_infer_types() && is_new_pred { debug!( @@ -752,7 +751,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // when we started out trying to unify // some inference variables. See the comment above // for more infomration - if p.ty().skip_binder().has_infer_types() { + if p.term().skip_binder().has_infer_types() { if !self.evaluate_nested_obligations( ty, v.into_iter(), @@ -774,7 +773,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // However, we should always make progress (either by generating // subobligations or getting an error) when we started off with // inference variables - if p.ty().skip_binder().has_infer_types() { + if p.term().skip_binder().has_infer_types() { panic!("Unexpected result when selecting {:?} {:?}", ty, obligation) } } @@ -810,14 +809,14 @@ impl<'tcx> AutoTraitFinder<'tcx> { }; } ty::PredicateKind::ConstEquate(c1, c2) => { - let evaluate = |c: &'tcx ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(unevaluated) = c.val { + let evaluate = |c: ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(unevaluated) = c.val() { match select.infcx().const_eval_resolve( obligation.param_env, unevaluated, Some(obligation.cause.span), ) { - Ok(val) => Ok(ty::Const::from_value(select.tcx(), val, c.ty)), + Ok(val) => Ok(ty::Const::from_value(select.tcx(), val, c.ty())), Err(err) => Err(err), } } else { @@ -876,8 +875,8 @@ 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(), + (match *r { + 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/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 34fc4ca8fe..93c2f20254 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -8,7 +8,7 @@ use crate::traits::{ PredicateObligation, SelectionError, TraitEngine, }; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; pub struct FulfillmentContext<'tcx> { obligations: FxIndexSet>, @@ -91,7 +91,12 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { let environment = obligation.param_env.caller_bounds(); let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate }; let mut orig_values = OriginalQueryValues::default(); - let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values); + if goal.references_error() { + continue; + } + + let canonical_goal = + infcx.canonicalize_query_preserving_universes(goal, &mut orig_values); match infcx.tcx.evaluate_goal(canonical_goal) { Ok(response) => { diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index 848aba7c91..bf6e6a4fcb 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -18,12 +18,11 @@ use rustc_middle::ty::{self, TyCtxt}; /// that type check should guarantee to us that all nested /// obligations *could be* resolved if we wanted to. /// -/// Assumes that this is run after the entire crate has been successfully type-checked. /// This also expects that `trait_ref` is fully normalized. pub fn codegen_fulfill_obligation<'tcx>( tcx: TyCtxt<'tcx>, (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), -) -> Result, ErrorReported> { +) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorReported> { // Remove any references to regions; this helps improve caching. let trait_ref = tcx.erase_regions(trait_ref); // We expect the input to be fully normalized. @@ -65,6 +64,8 @@ pub fn codegen_fulfill_obligation<'tcx>( Err(Unimplemented) => { // This can trigger when we probe for the source of a `'static` lifetime requirement // on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound. + // This can also trigger when we have a global bound that is not actually satisfied, + // but was included during typeck due to the trivial_bounds feature. infcx.tcx.sess.delay_span_bug( rustc_span::DUMMY_SP, &format!( @@ -92,7 +93,7 @@ pub fn codegen_fulfill_obligation<'tcx>( let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, impl_source); debug!("Cache miss: {:?} => {:?}", trait_ref, impl_source); - Ok(impl_source) + Ok(&*tcx.arena.alloc(impl_source)) }) } @@ -101,7 +102,7 @@ pub fn codegen_fulfill_obligation<'tcx>( /// Finishes processes any obligations that remain in the /// fulfillment context, and then returns the result with all type /// variables removed and regions erased. Because this is intended -/// for use after type-check has completed, if any errors occur, +/// for use outside of type inference, if any errors occur, /// it will panic. It is used during normalization and other cases /// where processing the obligations in `fulfill_cx` may cause /// type inference variables that appear in `result` to be @@ -124,7 +125,10 @@ where if !errors.is_empty() { infcx.tcx.sess.delay_span_bug( rustc_span::DUMMY_SP, - &format!("Encountered errors `{:?}` resolving bounds after type-checking", errors), + &format!( + "Encountered errors `{:?}` resolving bounds outside of type inference", + errors + ), ); } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 290426aa82..b2aa72e0e6 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -4,15 +4,22 @@ //! [trait-resolution]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html //! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html -use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt}; -use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::infer::outlives::env::OutlivesEnvironment; +use crate::infer::{CombinedSnapshot, InferOk, RegionckMode}; use crate::traits::select::IntercrateAmbiguityCause; +use crate::traits::util::impl_trait_ref_and_oblig; use crate::traits::SkipLeakCheck; use crate::traits::{ - self, Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext, + self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation, + PredicateObligations, SelectionContext, }; +//use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences}; +use rustc_hir::CRATE_HIR_ID; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::TraitEngine; +use rustc_middle::traits::specialization_graph::OverlapMode; +use rustc_middle::ty::fast_reject::{self, SimplifyParams}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -53,11 +60,13 @@ pub fn add_placeholder_note(err: &mut rustc_errors::DiagnosticBuilder<'_>) { /// If there are types that satisfy both impls, invokes `on_overlap` /// with a suitably-freshened `ImplHeader` with those types /// substituted. Otherwise, invokes `no_overlap`. +#[instrument(skip(tcx, skip_leak_check, on_overlap, no_overlap), level = "debug")] pub fn overlapping_impls( tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId, skip_leak_check: SkipLeakCheck, + overlap_mode: OverlapMode, on_overlap: F1, no_overlap: F2, ) -> R @@ -65,12 +74,6 @@ where F1: FnOnce(OverlapResult<'_>) -> R, F2: FnOnce() -> R, { - debug!( - "overlapping_impls(\ - impl1_def_id={:?}, \ - impl2_def_id={:?})", - impl1_def_id, impl2_def_id, - ); // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. @@ -83,8 +86,9 @@ where impl2_ref.iter().flat_map(|tref| tref.substs.types()), ) .any(|(ty1, ty2)| { - let t1 = fast_reject::simplify_type(tcx, ty1, SimplifyParams::No, StripReferences::No); - let t2 = fast_reject::simplify_type(tcx, ty2, SimplifyParams::No, StripReferences::No); + let t1 = fast_reject::simplify_type(tcx, ty1, SimplifyParams::No); + let t2 = fast_reject::simplify_type(tcx, ty2, SimplifyParams::No); + if let (Some(t1), Some(t2)) = (t1, t2) { // Simplified successfully t1 != t2 @@ -100,7 +104,7 @@ where let overlaps = tcx.infer_ctxt().enter(|infcx| { let selcx = &mut SelectionContext::intercrate(&infcx); - overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some() + overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some() }); if !overlaps { @@ -113,7 +117,9 @@ where tcx.infer_ctxt().enter(|infcx| { let selcx = &mut SelectionContext::intercrate(&infcx); selcx.enable_tracking_intercrate_ambiguity_causes(); - on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap()) + on_overlap( + overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap(), + ) }) } @@ -144,40 +150,43 @@ fn with_fresh_ty_vars<'cx, 'tcx>( fn overlap<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, skip_leak_check: SkipLeakCheck, - a_def_id: DefId, - b_def_id: DefId, + impl1_def_id: DefId, + impl2_def_id: DefId, + overlap_mode: OverlapMode, ) -> Option> { - debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id); + debug!( + "overlap(impl1_def_id={:?}, impl2_def_id={:?}, overlap_mode={:?})", + impl1_def_id, impl2_def_id, overlap_mode + ); selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| { - overlap_within_probe(selcx, skip_leak_check, a_def_id, b_def_id, snapshot) + overlap_within_probe( + selcx, + skip_leak_check, + impl1_def_id, + impl2_def_id, + overlap_mode, + snapshot, + ) }) } fn overlap_within_probe<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, skip_leak_check: SkipLeakCheck, - a_def_id: DefId, - b_def_id: DefId, + impl1_def_id: DefId, + impl2_def_id: DefId, + overlap_mode: OverlapMode, snapshot: &CombinedSnapshot<'_, 'tcx>, ) -> Option> { - fn loose_check<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - o: &PredicateObligation<'tcx>, - ) -> bool { - !selcx.predicate_may_hold_fatal(o) - } + let infcx = selcx.infcx(); - fn strict_check<'cx, 'tcx>( - selcx: &SelectionContext<'cx, 'tcx>, - o: &PredicateObligation<'tcx>, - ) -> bool { - let infcx = selcx.infcx(); - let tcx = infcx.tcx; - o.flip_polarity(tcx) - .as_ref() - .map(|o| selcx.infcx().predicate_must_hold_modulo_regions(o)) - .unwrap_or(false) + if overlap_mode.use_negative_impl() { + if negative_impl(selcx, impl1_def_id, impl2_def_id) + || negative_impl(selcx, impl2_def_id, impl1_def_id) + { + return None; + } } // For the purposes of this check, we don't bring any placeholder @@ -186,26 +195,59 @@ fn overlap_within_probe<'cx, 'tcx>( // empty environment. let param_env = ty::ParamEnv::empty(); - let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id); - let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id); + let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id); + let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id); - debug!("overlap: a_impl_header={:?}", a_impl_header); - debug!("overlap: b_impl_header={:?}", b_impl_header); + let obligations = equate_impl_headers(selcx, &impl1_header, &impl2_header)?; + debug!("overlap: unification check succeeded"); - // Do `a` and `b` unify? If not, no overlap. - let obligations = match selcx - .infcx() - .at(&ObligationCause::dummy(), param_env) - .eq_impl_headers(&a_impl_header, &b_impl_header) - { - Ok(InferOk { obligations, value: () }) => obligations, - Err(_) => { + if overlap_mode.use_implicit_negative() { + if implicit_negative(selcx, param_env, &impl1_header, impl2_header, obligations) { return None; } - }; + } - debug!("overlap: unification check succeeded"); + if !skip_leak_check.is_yes() { + if infcx.leak_check(true, snapshot).is_err() { + debug!("overlap: leak check failed"); + return None; + } + } + + let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes(); + debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); + + let involves_placeholder = + matches!(selcx.infcx().region_constraints_added_in_snapshot(snapshot), Some(true)); + + let impl_header = selcx.infcx().resolve_vars_if_possible(impl1_header); + Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) +} + +fn equate_impl_headers<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + impl1_header: &ty::ImplHeader<'tcx>, + impl2_header: &ty::ImplHeader<'tcx>, +) -> Option> { + // Do `a` and `b` unify? If not, no overlap. + debug!("equate_impl_headers(impl1_header={:?}, impl2_header={:?}", impl1_header, impl2_header); + selcx + .infcx() + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .eq_impl_headers(impl1_header, impl2_header) + .map(|infer_ok| infer_ok.obligations) + .ok() +} +/// Given impl1 and impl2 check if both impls can be satisfied by a common type (including +/// where-clauses) If so, return false, otherwise return true, they are disjoint. +fn implicit_negative<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl1_header: &ty::ImplHeader<'tcx>, + impl2_header: ty::ImplHeader<'tcx>, + obligations: PredicateObligations<'tcx>, +) -> bool { // There's no overlap if obligations are unsatisfiable or if the obligation negated is // satisfied. // @@ -227,13 +269,16 @@ fn overlap_within_probe<'cx, 'tcx>( // If the obligation `&'?a str: Error` holds, it means that there's overlap. If that doesn't // hold we need to check if `&'?a str: !Error` holds, if doesn't hold there's overlap because // at some point an impl for `&'?a str: Error` could be added. + debug!( + "implicit_negative(impl1_header={:?}, impl2_header={:?}, obligations={:?})", + impl1_header, impl2_header, obligations + ); let infcx = selcx.infcx(); - let tcx = infcx.tcx; - let opt_failing_obligation = a_impl_header + let opt_failing_obligation = impl1_header .predicates .iter() .copied() - .chain(b_impl_header.predicates) + .chain(impl2_header.predicates) .map(|p| infcx.resolve_vars_if_possible(p)) .map(|p| Obligation { cause: ObligationCause::dummy(), @@ -242,41 +287,125 @@ fn overlap_within_probe<'cx, 'tcx>( predicate: p, }) .chain(obligations) - .find(|o| { - // if both impl headers are set to strict coherence it means that this will be accepted - // only if it's stated that T: !Trait. So only prove that the negated obligation holds. - if tcx.has_attr(a_def_id, sym::rustc_strict_coherence) - && tcx.has_attr(b_def_id, sym::rustc_strict_coherence) - { - strict_check(selcx, o) - } else { - loose_check(selcx, o) || tcx.features().negative_impls && strict_check(selcx, o) - } - }); - // FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported - // to the canonical trait query form, `infcx.predicate_may_hold`, once - // the new system supports intercrate mode (which coherence needs). + .find(|o| !selcx.predicate_may_hold_fatal(o)); if let Some(failing_obligation) = opt_failing_obligation { debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); - return None; + true + } else { + false } +} - if !skip_leak_check.is_yes() { - if infcx.leak_check(true, snapshot).is_err() { - debug!("overlap: leak check failed"); - return None; +/// Given impl1 and impl2 check if both impls are never satisfied by a common type (including +/// where-clauses) If so, return true, they are disjoint and false otherwise. +fn negative_impl<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + impl1_def_id: DefId, + impl2_def_id: DefId, +) -> bool { + debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); + let tcx = selcx.infcx().tcx; + + // create a parameter environment corresponding to a (placeholder) instantiation of impl1 + let impl1_env = tcx.param_env(impl1_def_id); + let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); + + // Create an infcx, taking the predicates of impl1 as assumptions: + tcx.infer_ctxt().enter(|infcx| { + // Normalize the trait reference. The WF rules ought to ensure + // that this always succeeds. + let impl1_trait_ref = match traits::fully_normalize( + &infcx, + FulfillmentContext::new(), + ObligationCause::dummy(), + impl1_env, + impl1_trait_ref, + ) { + Ok(impl1_trait_ref) => impl1_trait_ref, + Err(err) => { + bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err); + } + }; + + // Attempt to prove that impl2 applies, given all of the above. + let selcx = &mut SelectionContext::new(&infcx); + let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); + let (impl2_trait_ref, obligations) = + impl_trait_ref_and_oblig(selcx, impl1_env, impl2_def_id, impl2_substs); + + // do the impls unify? If not, not disjoint. + let more_obligations = match infcx + .at(&ObligationCause::dummy(), impl1_env) + .eq(impl1_trait_ref, impl2_trait_ref) + { + Ok(InferOk { obligations, .. }) => obligations, + Err(_) => { + debug!( + "explicit_disjoint: {:?} does not unify with {:?}", + impl1_trait_ref, impl2_trait_ref + ); + return false; + } + }; + + let opt_failing_obligation = obligations + .into_iter() + .chain(more_obligations) + .find(|o| negative_impl_exists(selcx, impl1_env, impl1_def_id, o)); + + if let Some(failing_obligation) = opt_failing_obligation { + debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); + true + } else { + false } - } + }) +} - let impl_header = selcx.infcx().resolve_vars_if_possible(a_impl_header); - let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes(); - debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); +fn negative_impl_exists<'cx, 'tcx>( + selcx: &SelectionContext<'cx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + region_context: DefId, + o: &PredicateObligation<'tcx>, +) -> bool { + let infcx = &selcx.infcx().fork(); + let tcx = infcx.tcx; + o.flip_polarity(tcx) + .map(|o| { + let mut fulfillment_cx = FulfillmentContext::new(); + fulfillment_cx.register_predicate_obligation(infcx, o); + + let errors = fulfillment_cx.select_all_or_error(infcx); + if !errors.is_empty() { + return false; + } - let involves_placeholder = - matches!(selcx.infcx().region_constraints_added_in_snapshot(snapshot), Some(true)); + let mut outlives_env = OutlivesEnvironment::new(param_env); + // FIXME -- add "assumed to be well formed" types into the `outlives_env` + + // "Save" the accumulated implied bounds into the outlives environment + // (due to the FIXME above, there aren't any, but this step is still needed). + // The "body id" is given as `CRATE_HIR_ID`, which is the same body-id used + // by the "dummy" causes elsewhere (body-id is only relevant when checking + // function bodies with closures). + outlives_env.save_implied_bounds(CRATE_HIR_ID); + + infcx.process_registered_region_obligations( + outlives_env.region_bound_pairs_map(), + Some(tcx.lifetimes.re_root_empty), + param_env, + ); + + let errors = + infcx.resolve_regions(region_context, &outlives_env, RegionckMode::default()); + if !errors.is_empty() { + return false; + } - Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) + true + }) + .unwrap_or(false) } pub fn trait_ref_is_knowable<'tcx>( @@ -444,7 +573,7 @@ fn orphan_check_trait_ref<'tcx>( ) -> Result<(), OrphanCheckErr<'tcx>> { debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})", trait_ref, in_crate); - if trait_ref.needs_infer() && trait_ref.definitely_needs_subst(tcx) { + if trait_ref.needs_infer() && trait_ref.needs_subst() { bug!( "can't orphan check a trait ref with both params and inference variables {:?}", trait_ref @@ -497,7 +626,7 @@ fn orphan_check_trait_ref<'tcx>( .substs .types() .flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate)) - .find(|ty| ty_is_local_constructor(ty, in_crate)); + .find(|ty| ty_is_local_constructor(*ty, in_crate)); debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type); diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 0ea3a18ca3..1994faed70 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -8,6 +8,7 @@ //! In this case we try to build an abstract representation of this constant using //! `thir_abstract_const` which can then be checked for structural equality with other //! generic constants mentioned in the `caller_bounds` of the current environment. +use rustc_data_structures::intern::Interned; use rustc_errors::ErrorReported; use rustc_hir::def::DefKind; use rustc_index::vec::IndexVec; @@ -84,7 +85,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( Node::Leaf(leaf) => { if leaf.has_infer_types_or_consts() { failure_kind = FailureKind::MentionsInfer; - } else if leaf.definitely_has_param_types_or_consts(tcx) { + } else if leaf.has_param_types_or_consts() { failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); } @@ -93,7 +94,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( Node::Cast(_, _, ty) => { if ty.has_infer_types_or_consts() { failure_kind = FailureKind::MentionsInfer; - } else if ty.definitely_has_param_types_or_consts(tcx) { + } else if ty.has_param_types_or_consts() { failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); } @@ -149,7 +150,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( // See #74595 for more details about this. let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); - if concrete.is_ok() && uv.substs(infcx.tcx).definitely_has_param_types_or_consts(infcx.tcx) { + if concrete.is_ok() && uv.substs.has_param_types_or_consts() { match infcx.tcx.def_kind(uv.def.did) { DefKind::AnonConst | DefKind::InlineConst => { let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def); @@ -196,14 +197,14 @@ impl<'tcx> AbstractConst<'tcx> { ) -> Result>, ErrorReported> { let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?; debug!("AbstractConst::new({:?}) = {:?}", uv, inner); - Ok(inner.map(|inner| AbstractConst { inner, substs: uv.substs(tcx) })) + Ok(inner.map(|inner| AbstractConst { inner, substs: uv.substs })) } pub fn from_const( tcx: TyCtxt<'tcx>, - ct: &ty::Const<'tcx>, + ct: ty::Const<'tcx>, ) -> Result>, ErrorReported> { - match ct.val { + match ct.val() { ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()), ty::ConstKind::Error(_) => Err(ErrorReported), _ => Ok(None), @@ -271,7 +272,6 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { struct IsThirPolymorphic<'a, 'tcx> { is_poly: bool, thir: &'a thir::Thir<'tcx>, - tcx: TyCtxt<'tcx>, } use thir::visit; @@ -281,25 +281,25 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { } fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { - self.is_poly |= expr.ty.definitely_has_param_types_or_consts(self.tcx); + self.is_poly |= expr.ty.has_param_types_or_consts(); if !self.is_poly { visit::walk_expr(self, expr) } } fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { - self.is_poly |= pat.ty.definitely_has_param_types_or_consts(self.tcx); + self.is_poly |= pat.ty.has_param_types_or_consts(); if !self.is_poly { visit::walk_pat(self, pat); } } - fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) { - self.is_poly |= ct.definitely_has_param_types_or_consts(self.tcx); + fn visit_const(&mut self, ct: ty::Const<'tcx>) { + self.is_poly |= ct.has_param_types_or_consts(); } } - let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body, tcx }; + let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; visit::walk_expr(&mut is_poly_vis, &body[body_id]); debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly); if !is_poly_vis.is_poly { @@ -335,7 +335,11 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { self.recurse_build(self.body_id)?; for n in self.nodes.iter() { - if let Node::Leaf(ty::Const { val: ty::ConstKind::Unevaluated(ct), ty: _ }) = n { + if let Node::Leaf(ty::Const(Interned( + ty::ConstS { val: ty::ConstKind::Unevaluated(ct), ty: _ }, + _, + ))) = n + { // `AbstractConst`s should not contain any promoteds as they require references which // are not allowed. assert_eq!(ct.promoted, None); @@ -480,7 +484,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { // let expressions imply control flow ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => self.error(node.span, "control flow is not supported in generic constants")?, - ExprKind::LlvmInlineAsm { .. } | ExprKind::InlineAsm { .. } => { + ExprKind::InlineAsm { .. } => { self.error(node.span, "assembly is not supported in generic constants")? } @@ -603,11 +607,11 @@ pub(super) fn try_unify<'tcx>( match (a.root(tcx), b.root(tcx)) { (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - if a_ct.ty != b_ct.ty { + if a_ct.ty() != b_ct.ty() { return false; } - match (a_ct.val, b_ct.val) { + match (a_ct.val(), b_ct.val()) { // We can just unify errors with everything to reduce the amount of // emitted errors here. (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, 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 a9ae0ec53c..c3df17c83b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -21,10 +21,9 @@ use rustc_hir::Item; use rustc_hir::Node; use rustc_middle::thir::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences}; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::{ - self, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, + self, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, }; use rustc_session::DiagnosticMessageId; use rustc_span::symbol::{kw, sym}; @@ -40,6 +39,22 @@ use suggestions::InferCtxtExt as _; pub use rustc_infer::traits::error_reporting::*; +// When outputting impl candidates, prefer showing those that are more similar. +// +// We also compare candidates after skipping lifetimes, which has a lower +// priority than exact matches. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum CandidateSimilarity { + Exact { ignoring_lifetimes: bool }, + Fuzzy { ignoring_lifetimes: bool }, +} + +#[derive(Debug, Clone, Copy)] +pub struct ImplCandidate<'tcx> { + pub trait_ref: ty::TraitRef<'tcx>, + pub similarity: CandidateSimilarity, +} + pub trait InferCtxtExt<'tcx> { fn report_fulfillment_errors( &self, @@ -205,6 +220,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.note_obligation_cause_code( &mut err, &obligation.predicate, + obligation.param_env, obligation.cause.code(), &mut vec![], &mut Default::default(), @@ -261,7 +277,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .tcx .diagnostic_hir_wf_check((tcx.erase_regions(obligation.predicate), *wf_loc)) { - obligation.cause = cause; + obligation.cause = cause.clone(); span = obligation.cause.span; } } @@ -288,7 +304,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { match bound_predicate.skip_binder() { ty::PredicateKind::Trait(trait_predicate) => { let trait_predicate = bound_predicate.rebind(trait_predicate); - let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate); + + trait_predicate.remap_constness_diag(obligation.param_env); + let predicate_is_const = ty::BoundConstness::ConstIfConst + == trait_predicate.skip_binder().constness; if self.tcx.sess.has_errors() && trait_predicate.references_error() { return; @@ -305,13 +325,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }) .unwrap_or_default(); - let OnUnimplementedNote { message, label, note, enclosing_scope } = - self.on_unimplemented_note(trait_ref, &obligation); + let OnUnimplementedNote { + message, + label, + note, + enclosing_scope, + append_const_msg, + } = self.on_unimplemented_note(trait_ref, &obligation); let have_alt_message = message.is_some() || label.is_some(); 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_conversion { + let (message, note, append_const_msg) = if is_try_conversion { ( Some(format!( "`?` couldn't convert the error to `{}`", @@ -322,9 +347,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { conversion on the error value using the `From` trait" .to_owned(), ), + Some(None), ) } else { - (message, note) + (message, note, append_const_msg) }; let mut err = struct_span_err!( @@ -332,11 +358,27 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span, E0277, "{}", - message.unwrap_or_else(|| format!( - "the trait bound `{}` is not satisfied{}", - trait_ref.without_const().to_predicate(tcx), - post_message, - )) + message + .and_then(|cannot_do_this| { + match (predicate_is_const, append_const_msg) { + // do nothing if predicate is not const + (false, _) => Some(cannot_do_this), + // suggested using default post message + (true, Some(None)) => { + Some(format!("{cannot_do_this} in const contexts")) + } + // overriden post message + (true, Some(Some(post_message))) => { + Some(format!("{cannot_do_this}{post_message}")) + } + // fallback to generic message + (true, None) => None, + } + }) + .unwrap_or_else(|| format!( + "the trait bound `{}` is not satisfied{}", + trait_predicate, post_message, + )) ); if is_try_conversion { @@ -384,7 +426,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { format!( "{}the trait `{}` is not implemented for `{}`", pre_message, - trait_ref.print_only_trait_path(), + trait_predicate.print_modifiers_and_trait_path(), trait_ref.skip_binder().self_ty(), ) }; @@ -392,7 +434,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if self.suggest_add_reference_to_arg( &obligation, &mut err, - &trait_ref, + trait_predicate, have_alt_message, ) { self.note_obligation_cause(&mut err, &obligation); @@ -412,6 +454,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } else { err.span_label(span, explanation); } + + if trait_predicate.is_const_if_const() && obligation.param_env.is_const() { + let non_const_predicate = trait_ref.without_const(); + let non_const_obligation = Obligation { + cause: obligation.cause.clone(), + param_env: obligation.param_env.without_const(), + predicate: non_const_predicate.to_predicate(tcx), + recursion_depth: obligation.recursion_depth, + }; + if self.predicate_may_hold(&non_const_obligation) { + err.span_note( + span, + &format!( + "the trait `{}` is implemented for `{}`, \ + but that implementation is not `const`", + non_const_predicate.print_modifiers_and_trait_path(), + trait_ref.skip_binder().self_ty(), + ), + ); + } + } + if let Some((msg, span)) = type_def { err.span_label(span, &msg); } @@ -435,18 +499,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_label(enclosing_scope_span, s.as_str()); } - self.suggest_dereferences(&obligation, &mut err, trait_ref); - self.suggest_fn_call(&obligation, &mut err, trait_ref); - self.suggest_remove_reference(&obligation, &mut err, trait_ref); - self.suggest_semicolon_removal(&obligation, &mut err, span, trait_ref); + self.suggest_dereferences(&obligation, &mut err, trait_predicate); + self.suggest_fn_call(&obligation, &mut err, trait_predicate); + self.suggest_remove_reference(&obligation, &mut err, trait_predicate); + self.suggest_semicolon_removal( + &obligation, + &mut err, + span, + trait_predicate, + ); self.note_version_mismatch(&mut err, &trait_ref); self.suggest_remove_await(&obligation, &mut err); if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { - self.suggest_await_before_try(&mut err, &obligation, trait_ref, span); + self.suggest_await_before_try( + &mut err, + &obligation, + trait_predicate, + span, + ); } - if self.suggest_impl_trait(&mut err, span, &obligation, trait_ref) { + if self.suggest_impl_trait(&mut err, span, &obligation, trait_predicate) { err.emit(); return; } @@ -494,7 +568,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // which is somewhat confusing. self.suggest_restricting_param_bound( &mut err, - trait_ref, + trait_predicate, obligation.cause.body_id, ); } else if !have_alt_message { @@ -506,7 +580,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Changing mutability doesn't make a difference to whether we have // an `Unsize` impl (Fixes ICE in #71036) if !is_unsize { - self.suggest_change_mut(&obligation, &mut err, trait_ref); + self.suggest_change_mut(&obligation, &mut err, trait_predicate); } // If this error is due to `!: Trait` not implemented but `(): Trait` is @@ -616,8 +690,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.sess.source_map().guess_head_span( self.tcx.hir().span_if_local(closure_def_id).unwrap(), ); - let hir_id = - self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); let mut err = struct_span_err!( self.tcx.sess, closure_span, @@ -640,6 +712,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Additional context information explaining why the closure only implements // a particular trait. if let Some(typeck_results) = self.in_progress_typeck_results { + let hir_id = self + .tcx + .hir() + .local_def_id_to_hir_id(closure_def_id.expect_local()); let typeck_results = typeck_results.borrow(); match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { (ty::ClosureKind::FnOnce, Some((span, place))) => { @@ -1082,18 +1158,23 @@ trait InferCtxtPrivExt<'hir, 'tcx> { error: &MismatchedProjectionTypes<'tcx>, ); - fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool; + fn fuzzy_match_tys( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + ignoring_lifetimes: bool, + ) -> Option; fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str>; fn find_similar_impl_candidates( &self, trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Vec>; + ) -> Vec>; fn report_similar_impl_candidates( &self, - impl_candidates: Vec>, + impl_candidates: Vec>, err: &mut DiagnosticBuilder<'_>, ); @@ -1119,7 +1200,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn mk_trait_obligation_with_new_self_ty( &self, param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::PolyTraitPredicate<'tcx>, new_self_ty: Ty<'tcx>, ) -> PredicateObligation<'tcx>; @@ -1170,7 +1251,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn is_recursive_obligation( &self, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, + obligated_types: &mut Vec>, cause_code: &ObligationCauseCode<'tcx>, ) -> bool; } @@ -1302,7 +1383,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { debug!( "report_projection_error normalized_ty={:?} data.ty={:?}", - normalized_ty, data.ty + normalized_ty, data.term, ); let is_normalized_ty_expected = !matches!( @@ -1312,18 +1393,16 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::ObjectCastObligation(_) | ObligationCauseCode::OpaqueType ); - if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( is_normalized_ty_expected, normalized_ty, - data.ty, + data.term, ) { - values = Some(infer::ValuePairs::Types(ExpectedFound::new( + values = Some(infer::ValuePairs::Terms(ExpectedFound::new( is_normalized_ty_expected, normalized_ty, - data.ty, + data.term, ))); - err_buf = error; err = &err_buf; } @@ -1350,6 +1429,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { .map(|id| (trait_assoc_item, id)) }) .and_then(|(trait_assoc_item, id)| { + let trait_assoc_ident = trait_assoc_item.ident(self.tcx); self.tcx.find_map_relevant_impl( id, proj.projection_ty.self_ty(), @@ -1357,8 +1437,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { self.tcx .associated_items(did) .in_definition_order() - .filter(|assoc| assoc.ident == trait_assoc_item.ident) - .next() + .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident) }, ) }) @@ -1386,45 +1465,80 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { }); } - fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + fn fuzzy_match_tys( + &self, + mut a: Ty<'tcx>, + mut b: Ty<'tcx>, + ignoring_lifetimes: bool, + ) -> Option { /// returns the fuzzy category of a given type, or None /// if the type can be equated to any type. - fn type_category(t: Ty<'_>) -> Option { + fn type_category(tcx: TyCtxt<'_>, t: Ty<'_>) -> Option { match t.kind() { ty::Bool => Some(0), ty::Char => Some(1), ty::Str => Some(2), - ty::Int(..) | ty::Uint(..) | ty::Infer(ty::IntVar(..)) => Some(3), - ty::Float(..) | ty::Infer(ty::FloatVar(..)) => Some(4), + ty::Adt(def, _) if tcx.is_diagnostic_item(sym::String, def.did) => Some(2), + ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) => Some(4), ty::Ref(..) | ty::RawPtr(..) => Some(5), ty::Array(..) | ty::Slice(..) => Some(6), ty::FnDef(..) | ty::FnPtr(..) => Some(7), ty::Dynamic(..) => Some(8), ty::Closure(..) => Some(9), ty::Tuple(..) => Some(10), - ty::Projection(..) => Some(11), - ty::Param(..) => Some(12), + ty::Param(..) => Some(11), + ty::Projection(..) => Some(12), ty::Opaque(..) => Some(13), ty::Never => Some(14), - ty::Adt(adt, ..) => match adt.adt_kind() { - AdtKind::Struct => Some(15), - AdtKind::Union => Some(16), - AdtKind::Enum => Some(17), - }, - ty::Generator(..) => Some(18), - ty::Foreign(..) => Some(19), - ty::GeneratorWitness(..) => Some(20), + ty::Adt(..) => Some(15), + ty::Generator(..) => Some(16), + ty::Foreign(..) => Some(17), + ty::GeneratorWitness(..) => Some(18), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } - match (type_category(a), type_category(b)) { - (Some(cat_a), Some(cat_b)) => match (a.kind(), b.kind()) { - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b, - _ => cat_a == cat_b, - }, - // infer and error can be equated to all types - _ => true, + let strip_references = |mut t: Ty<'tcx>| -> Ty<'tcx> { + loop { + match t.kind() { + ty::Ref(_, inner, _) | ty::RawPtr(ty::TypeAndMut { ty: inner, .. }) => { + t = *inner + } + _ => break t, + } + } + }; + + if !ignoring_lifetimes { + a = strip_references(a); + b = strip_references(b); + } + + let cat_a = type_category(self.tcx, a)?; + let cat_b = type_category(self.tcx, b)?; + if a == b { + Some(CandidateSimilarity::Exact { ignoring_lifetimes }) + } else if cat_a == cat_b { + match (a.kind(), b.kind()) { + (ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b, + // Matching on references results in a lot of unhelpful + // suggestions, so let's just not do that for now. + // + // We still upgrade successful matches to `ignoring_lifetimes: true` + // to prioritize that impl. + (ty::Ref(..) | ty::RawPtr(..), ty::Ref(..) | ty::RawPtr(..)) => { + self.fuzzy_match_tys(a, b, true).is_some() + } + _ => true, + } + .then_some(CandidateSimilarity::Fuzzy { ignoring_lifetimes }) + } else if ignoring_lifetimes { + None + } else { + self.fuzzy_match_tys(a, b, true) } } @@ -1440,58 +1554,25 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn find_similar_impl_candidates( &self, trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Vec> { - // We simplify params and strip references here. - // - // This both removes a lot of unhelpful suggestions, e.g. - // when searching for `&Foo: Trait` it doesn't suggestion `impl Trait for &Bar`, - // while also suggesting impls for `&Foo` when we're looking for `Foo: Trait`. - // - // The second thing isn't necessarily always a good thing, but - // any other simple setup results in a far worse output, so 🤷 - let simp = fast_reject::simplify_type( - self.tcx, - trait_ref.skip_binder().self_ty(), - SimplifyParams::Yes, - StripReferences::Yes, - ); - let all_impls = self.tcx.all_impls(trait_ref.def_id()); - - match simp { - Some(simp) => all_impls - .filter_map(|def_id| { - let imp = self.tcx.impl_trait_ref(def_id).unwrap(); - let imp_simp = fast_reject::simplify_type( - self.tcx, - imp.self_ty(), - SimplifyParams::Yes, - StripReferences::Yes, - ); - if let Some(imp_simp) = imp_simp { - if simp != imp_simp { - return None; - } - } - if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative { - return None; - } - Some(imp) - }) - .collect(), - None => all_impls - .filter_map(|def_id| { - if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative { - return None; - } - self.tcx.impl_trait_ref(def_id) - }) - .collect(), - } + ) -> Vec> { + self.tcx + .all_impls(trait_ref.def_id()) + .filter_map(|def_id| { + if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative { + return None; + } + + let imp = self.tcx.impl_trait_ref(def_id).unwrap(); + + self.fuzzy_match_tys(trait_ref.skip_binder().self_ty(), imp.self_ty(), false) + .map(|similarity| ImplCandidate { trait_ref: imp, similarity }) + }) + .collect() } fn report_similar_impl_candidates( &self, - impl_candidates: Vec>, + impl_candidates: Vec>, err: &mut DiagnosticBuilder<'_>, ) { if impl_candidates.is_empty() { @@ -1515,13 +1596,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { }; // Sort impl candidates so that ordering is consistent for UI tests. - let mut normalized_impl_candidates = - impl_candidates.iter().copied().map(normalize).collect::>(); - - // Sort before taking the `..end` range, // because the ordering of `impl_candidates` may not be deterministic: // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 - normalized_impl_candidates.sort(); + // + // Prefer more similar candidates first, then sort lexicographically + // by their normalized string representation. + let mut normalized_impl_candidates_and_similarities = impl_candidates + .into_iter() + .map(|ImplCandidate { trait_ref, similarity }| { + let normalized = normalize(trait_ref); + (similarity, normalized) + }) + .collect::>(); + normalized_impl_candidates_and_similarities.sort(); + + let normalized_impl_candidates = normalized_impl_candidates_and_similarities + .into_iter() + .map(|(_, normalized)| normalized) + .collect::>(); err.help(&format!( "the following implementations were found:{}{}", @@ -1537,7 +1629,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ) -> Option<(String, Option)> { match code { ObligationCauseCode::BuiltinDerivedObligation(data) => { - let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); match self.get_parent_trait_ref(&data.parent_code) { Some(t) => Some(t), None => { @@ -1590,21 +1682,20 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn mk_trait_obligation_with_new_self_ty( &self, param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::PolyTraitPredicate<'tcx>, new_self_ty: Ty<'tcx>, ) -> PredicateObligation<'tcx> { assert!(!new_self_ty.has_escaping_bound_vars()); - let trait_ref = trait_ref.map_bound_ref(|tr| ty::TraitRef { - substs: self.tcx.mk_substs_trait(new_self_ty, &tr.substs[1..]), + let trait_pred = trait_ref.map_bound_ref(|tr| ty::TraitPredicate { + trait_ref: ty::TraitRef { + substs: self.tcx.mk_substs_trait(new_self_ty, &tr.trait_ref.substs[1..]), + ..tr.trait_ref + }, ..*tr }); - Obligation::new( - ObligationCause::dummy(), - param_env, - trait_ref.without_const().to_predicate(self.tcx), - ) + Obligation::new(ObligationCause::dummy(), param_env, trait_pred.to_predicate(self.tcx)) } #[instrument(skip(self), level = "debug")] @@ -1685,7 +1776,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { return; } - let impl_candidates = self.find_similar_impl_candidates(trait_ref); + let impl_candidates = self + .find_similar_impl_candidates(trait_ref) + .into_iter() + .map(|candidate| candidate.trait_ref) + .collect(); let mut err = self.emit_inference_failure_err( body_id, span, @@ -1801,11 +1896,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } ty::PredicateKind::Projection(data) => { let self_ty = data.projection_ty.self_ty(); - let ty = data.ty; + let term = data.term; if predicate.references_error() || self.is_tainted_by_errors() { return; } - if self_ty.needs_infer() && ty.needs_infer() { + if self_ty.needs_infer() && term.needs_infer() { // We do this for the `foo.collect()?` case to produce a suggestion. let mut err = self.emit_inference_failure_err( body_id, @@ -1958,7 +2053,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if let ty::Param(ty::ParamTy { name, .. }) = *ty.kind() { let infcx = self.infcx; - self.var_map.entry(ty).or_insert_with(|| { + *self.var_map.entry(ty).or_insert_with(|| { infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeParameterDefinition(name, None), span: DUMMY_SP, @@ -2005,6 +2100,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { self.note_obligation_cause_code( err, &obligation.predicate, + obligation.param_env, obligation.cause.code(), &mut vec![], &mut Default::default(), @@ -2148,11 +2244,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn is_recursive_obligation( &self, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, + obligated_types: &mut Vec>, cause_code: &ObligationCauseCode<'tcx>, ) -> bool { if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { - let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); let self_ty = parent_trait_ref.skip_binder().self_ty(); if obligated_types.iter().any(|ot| ot == &self_ty) { return true; @@ -2182,12 +2278,6 @@ struct FindTypeParam { } impl<'v> Visitor<'v> for FindTypeParam { - type Map = rustc_hir::intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::None - } - fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) { // Skip where-clauses, to avoid suggesting indirection for type parameters found there. } @@ -2247,7 +2337,7 @@ pub fn recursive_type_with_infinite_size_error( spans .iter() .flat_map(|&span| { - vec![ + [ (span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string()), ] diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 1540725246..6c8a08c09e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -56,7 +56,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref.substs.types().skip(1), impl_trait_ref.substs.types().skip(1), ) - .all(|(u, v)| self.fuzzy_match_tys(u, v)) + .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some()) { fuzzy_match_impls.push(def_id); } @@ -77,7 +77,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// Used to set on_unimplemented's `ItemContext` /// to be the enclosing (async) block/function/closure fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { - let hir = &self.tcx.hir(); + let hir = self.tcx.hir(); let node = hir.find(hir_id)?; match &node { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { @@ -211,7 +211,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let type_string = self.tcx.type_of(def.did).to_string(); flags.push((sym::_Self, Some(format!("[{}]", type_string)))); - let len = len.val.try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx)); + let len = + len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx)); let string = match len { Some(n) => format!("[{}; {}]", type_string, n), None => format!("[{}; _]", type_string), 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 0f276718c1..7df880a7cc 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -48,7 +48,7 @@ pub trait InferCtxtExt<'tcx> { fn suggest_restricting_param_bound( &self, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, body_id: hir::HirId, ); @@ -56,7 +56,7 @@ pub trait InferCtxtExt<'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ); fn get_closure_name( @@ -70,14 +70,14 @@ pub trait InferCtxtExt<'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ); fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, has_custom_message: bool, ) -> bool; @@ -85,7 +85,7 @@ pub trait InferCtxtExt<'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ); fn suggest_remove_await( @@ -98,7 +98,7 @@ pub trait InferCtxtExt<'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ); fn suggest_semicolon_removal( @@ -106,7 +106,7 @@ pub trait InferCtxtExt<'tcx> { obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, span: Span, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ); fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option; @@ -116,7 +116,7 @@ pub trait InferCtxtExt<'tcx> { err: &mut DiagnosticBuilder<'_>, span: Span, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool; fn point_at_returns_when_relevant( @@ -154,7 +154,7 @@ pub trait InferCtxtExt<'tcx> { interior_extra_info: Option<(Option, Span, Option, Option)>, inner_generator_body: Option<&hir::Body<'tcx>>, outer_generator: Option, - trait_ref: ty::TraitRef<'tcx>, + trait_pred: ty::TraitPredicate<'tcx>, target_ty: Ty<'tcx>, typeck_results: Option<&ty::TypeckResults<'tcx>>, obligation: &PredicateObligation<'tcx>, @@ -165,8 +165,9 @@ pub trait InferCtxtExt<'tcx> { &self, err: &mut DiagnosticBuilder<'_>, predicate: &T, + param_env: ty::ParamEnv<'tcx>, cause_code: &ObligationCauseCode<'tcx>, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, + obligated_types: &mut Vec>, seen_requirements: &mut FxHashSet, ) where T: fmt::Display; @@ -178,7 +179,7 @@ pub trait InferCtxtExt<'tcx> { &self, err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, span: Span, ); } @@ -204,7 +205,7 @@ fn suggest_restriction<'tcx>( err: &mut DiagnosticBuilder<'_>, fn_sig: Option<&hir::FnSig<'_>>, projection: Option<&ty::ProjectionTy<'_>>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>, ) { // When we are dealing with a trait, `super_traits` will be `Some`: @@ -257,9 +258,9 @@ fn suggest_restriction<'tcx>( // The type param `T: Trait` we will suggest to introduce. let type_param = format!("{}: {}", type_param_name, bound_str); - // FIXME: modify the `trait_ref` instead of string shenanigans. + // FIXME: modify the `trait_pred` instead of string shenanigans. // Turn `::Bar: Qux` into `::Bar: Qux`. - let pred = trait_ref.without_const().to_predicate(tcx).to_string(); + let pred = trait_pred.to_predicate(tcx).to_string(); let pred = pred.replace(&impl_trait_str, &type_param_name); let mut sugg = vec![ // Find the last of the generic parameters contained within the span of @@ -301,19 +302,19 @@ fn suggest_restriction<'tcx>( .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })), super_traits, ) { - (_, None) => predicate_constraint( - generics, - trait_ref.without_const().to_predicate(tcx).to_string(), + (_, None) => predicate_constraint(generics, trait_pred.to_predicate(tcx).to_string()), + (None, Some((ident, []))) => ( + ident.span.shrink_to_hi(), + format!(": {}", trait_pred.print_modifiers_and_trait_path()), + ), + (_, Some((_, [.., bounds]))) => ( + bounds.span().shrink_to_hi(), + format!(" + {}", trait_pred.print_modifiers_and_trait_path()), + ), + (Some(_), Some((_, []))) => ( + generics.span.shrink_to_hi(), + format!(": {}", trait_pred.print_modifiers_and_trait_path()), ), - (None, Some((ident, []))) => { - (ident.span.shrink_to_hi(), format!(": {}", trait_ref.print_only_trait_path())) - } - (_, Some((_, [.., bounds]))) => { - (bounds.span().shrink_to_hi(), format!(" + {}", trait_ref.print_only_trait_path())) - } - (Some(_), Some((_, []))) => { - (generics.span.shrink_to_hi(), format!(": {}", trait_ref.print_only_trait_path())) - } }; err.span_suggestion_verbose( @@ -329,10 +330,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn suggest_restricting_param_bound( &self, mut err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, body_id: hir::HirId, ) { - let self_ty = trait_ref.skip_binder().self_ty(); + let self_ty = trait_pred.skip_binder().self_ty(); let (param_ty, projection) = match self_ty.kind() { ty::Param(_) => (true, None), ty::Projection(projection) => (false, Some(projection)), @@ -358,7 +359,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err, None, projection, - trait_ref, + trait_pred, Some((ident, bounds)), ); return; @@ -372,7 +373,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { assert!(param_ty); // Restricting `Self` for a single method. suggest_restriction( - self.tcx, &generics, "`Self`", err, None, projection, trait_ref, None, + self.tcx, &generics, "`Self`", err, None, projection, trait_pred, None, ); return; } @@ -398,7 +399,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err, Some(fn_sig), projection, - trait_ref, + trait_pred, None, ); return; @@ -417,7 +418,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err, None, projection, - trait_ref, + trait_pred, None, ); return; @@ -442,15 +443,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { { // Missing generic type parameter bound. let param_name = self_ty.to_string(); - let constraint = - with_no_trimmed_paths(|| trait_ref.print_only_trait_path().to_string()); + let constraint = with_no_trimmed_paths(|| { + trait_pred.print_modifiers_and_trait_path().to_string() + }); if suggest_constraining_type_param( self.tcx, generics, &mut err, ¶m_name, &constraint, - Some(trait_ref.def_id()), + Some(trait_pred.def_id()), ) { return; } @@ -471,7 +473,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }) if !param_ty => { // Missing generic type parameter bound. let param_name = self_ty.to_string(); - let constraint = trait_ref.print_only_trait_path().to_string(); + let constraint = trait_pred.print_modifiers_and_trait_path().to_string(); if suggest_arbitrary_trait_bound(generics, &mut err, ¶m_name, &constraint) { return; } @@ -481,7 +483,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => {} } - hir_id = self.tcx.hir().get_parent_item(hir_id); + hir_id = self.tcx.hir().local_def_id_to_hir_id(self.tcx.hir().get_parent_item(hir_id)); } } @@ -492,7 +494,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) { // It only make sense when suggesting dereferences for arguments let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } = @@ -505,13 +507,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let param_env = obligation.param_env; let body_id = obligation.cause.body_id; let span = obligation.cause.span; - let real_trait_ref = match &*code { + let real_trait_pred = match &*code { ObligationCauseCode::ImplDerivedObligation(cause) | ObligationCauseCode::DerivedObligation(cause) - | ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_ref, - _ => trait_ref, + | ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_pred, + _ => trait_pred, }; - let real_ty = match real_trait_ref.self_ty().no_bound_vars() { + let real_ty = match real_trait_pred.self_ty().no_bound_vars() { Some(ty) => ty, None => return, }; @@ -522,7 +524,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Re-add the `&` let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); let obligation = - self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_ref, ty); + self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty); Some(steps).filter(|_| self.predicate_may_hold(&obligation)) }) { if steps > 0 { @@ -589,9 +591,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) { - let self_ty = match trait_ref.self_ty().no_bound_vars() { + let self_ty = match trait_pred.self_ty().no_bound_vars() { None => return, Some(ty) => ty, }; @@ -611,7 +613,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; let new_obligation = - self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_ref, output_ty); + self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred, output_ty); match self.evaluate_obligation(&new_obligation) { Ok( @@ -682,7 +684,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - poly_trait_ref: &ty::Binder<'tcx, ty::TraitRef<'tcx>>, + poly_trait_pred: ty::PolyTraitPredicate<'tcx>, has_custom_message: bool, ) -> bool { let span = obligation.cause.span; @@ -715,24 +717,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let param_env = obligation.param_env; // Try to apply the original trait binding obligation by borrowing. - let mut try_borrowing = |old_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool { - if blacklist.contains(&old_ref.def_id()) { + if blacklist.contains(&old_pred.def_id()) { return false; } - let orig_ty = old_ref.self_ty().skip_binder(); + let orig_ty = old_pred.self_ty().skip_binder(); let mk_result = |new_ty| { - let new_ref = old_ref.rebind(ty::TraitRef::new( - old_ref.def_id(), - self.tcx.mk_substs_trait(new_ty, &old_ref.skip_binder().substs[1..]), - )); - self.predicate_must_hold_modulo_regions(&Obligation::new( - ObligationCause::dummy(), - param_env, - new_ref.without_const().to_predicate(self.tcx), - )) + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, old_pred, new_ty); + self.predicate_must_hold_modulo_regions(&obligation) }; let imm_result = mk_result(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, orig_ty)); let mut_result = mk_result(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, orig_ty)); @@ -748,7 +744,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let msg = format!( "the trait bound `{}: {}` is not satisfied", orig_ty, - old_ref.print_only_trait_path(), + old_pred.print_modifiers_and_trait_path(), ); if has_custom_message { err.note(&msg); @@ -764,7 +760,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span, &format!( "expected an implementor of trait `{}`", - old_ref.print_only_trait_path(), + old_pred.print_modifiers_and_trait_path(), ), ); @@ -806,11 +802,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; if let ObligationCauseCode::ImplDerivedObligation(obligation) = code { - try_borrowing(obligation.parent_trait_ref, &[]) + try_borrowing(obligation.parent_trait_pred, &[]) } else if let ObligationCauseCode::BindingObligation(_, _) | ObligationCauseCode::ItemObligation(_) = code { - try_borrowing(*poly_trait_ref, &never_suggest_borrow) + try_borrowing(poly_trait_pred, &never_suggest_borrow) } else { false } @@ -822,7 +818,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let span = obligation.cause.span; @@ -834,45 +830,44 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - let mut suggested_ty = match trait_ref.self_ty().no_bound_vars() { + let mut suggested_ty = match trait_pred.self_ty().no_bound_vars() { Some(ty) => ty, None => return, }; for refs_remaining in 0..refs_number { - if let ty::Ref(_, inner_ty, _) = suggested_ty.kind() { - suggested_ty = inner_ty; + let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { + break; + }; + suggested_ty = *inner_ty; - let new_obligation = self.mk_trait_obligation_with_new_self_ty( - obligation.param_env, - trait_ref, - suggested_ty, - ); + let new_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred, + suggested_ty, + ); - if self.predicate_may_hold(&new_obligation) { - let sp = self - .tcx - .sess - .source_map() - .span_take_while(span, |c| c.is_whitespace() || *c == '&'); + if self.predicate_may_hold(&new_obligation) { + let sp = self + .tcx + .sess + .source_map() + .span_take_while(span, |c| c.is_whitespace() || *c == '&'); - let remove_refs = refs_remaining + 1; + let remove_refs = refs_remaining + 1; - let msg = if remove_refs == 1 { - "consider removing the leading `&`-reference".to_string() - } else { - format!("consider removing {} leading `&`-references", remove_refs) - }; + let msg = if remove_refs == 1 { + "consider removing the leading `&`-reference".to_string() + } else { + format!("consider removing {} leading `&`-references", remove_refs) + }; - err.span_suggestion_short( - sp, - &msg, - String::new(), - Applicability::MachineApplicable, - ); - break; - } - } else { + err.span_suggestion_short( + sp, + &msg, + String::new(), + Applicability::MachineApplicable, + ); break; } } @@ -942,7 +937,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let points_at_arg = matches!( obligation.cause.code(), @@ -957,14 +952,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Do not suggest removal of borrow from type arguments. return; } - let trait_ref = self.resolve_vars_if_possible(trait_ref); - if trait_ref.has_infer_types_or_consts() { + let trait_pred = self.resolve_vars_if_possible(trait_pred); + if trait_pred.has_infer_types_or_consts() { // Do not ICE while trying to find if a reborrow would succeed on a trait with // unresolved bindings. return; } - if let ty::Ref(region, t_type, mutability) = *trait_ref.skip_binder().self_ty().kind() { + if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind() + { if region.is_late_bound() || t_type.has_escaping_bound_vars() { // Avoid debug assertion in `mk_obligation_for_def_id`. // @@ -981,7 +977,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let new_obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, - trait_ref, + trait_pred, suggested_ty, ); let suggested_ty_would_satisfy_obligation = self @@ -1003,9 +999,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } else { err.note(&format!( "`{}` is implemented for `{:?}`, but not for `{:?}`", - trait_ref.print_only_trait_path(), + trait_pred.print_modifiers_and_trait_path(), suggested_ty, - trait_ref.skip_binder().self_ty(), + trait_pred.skip_binder().self_ty(), )); } } @@ -1018,7 +1014,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, span: Span, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let is_empty_tuple = |ty: ty::Binder<'tcx, Ty<'_>>| *ty.skip_binder().kind() == ty::Tuple(ty::List::empty()); @@ -1034,7 +1030,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let hir::ExprKind::Block(blk, _) = &body.value.kind { if sig.decl.output.span().overlaps(span) && blk.expr.is_none() - && is_empty_tuple(trait_ref.self_ty()) + && is_empty_tuple(trait_pred.self_ty()) { // FIXME(estebank): When encountering a method with a trait // bound not satisfied in the return type with a body that has @@ -1070,7 +1066,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, span: Span, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { match obligation.cause.code().peel_derives() { // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`. @@ -1089,8 +1085,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return false; }; let body = hir.body(*body_id); - let trait_ref = self.resolve_vars_if_possible(trait_ref); - let ty = trait_ref.skip_binder().self_ty(); + let trait_pred = self.resolve_vars_if_possible(trait_pred); + let ty = trait_pred.skip_binder().self_ty(); let is_object_safe = match ty.kind() { ty::Dynamic(predicates, _) => { // If the `dyn Trait` is not object safe, do not suggest `Box`. @@ -1103,9 +1099,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => return false, }; - let ret_ty = if let hir::FnRetTy::Return(ret_ty) = sig.decl.output { - ret_ty - } else { + let hir::FnRetTy::Return(ret_ty) = sig.decl.output else { return false; }; @@ -1172,7 +1166,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; let sm = self.tcx.sess.source_map(); - let snippet = if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true) = ( + let (true, hir::TyKind::TraitObject(..), Ok(snippet), true) = ( // Verify that we're dealing with a return `dyn Trait` ret_ty.span.overlaps(span), &ret_ty.kind, @@ -1180,9 +1174,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // If any of the return types does not conform to the trait, then we can't // suggest `impl Trait` nor trait objects: it is a type mismatch error. all_returns_conform_to_trait, - ) { - snippet - } else { + ) else { return false; }; err.code(error_code!(E0746)); @@ -1226,7 +1218,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .returns .iter() .flat_map(|expr| { - vec![ + [ (expr.span.shrink_to_lo(), "Box::new(".to_string()), (expr.span.shrink_to_hi(), ")".to_string()), ] @@ -1327,9 +1319,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref.rebind(sig).to_string() } - let argument_kind = match expected_ref.skip_binder().substs.type_at(0) { - t if t.is_closure() => "closure", - t if t.is_generator() => "generator", + let argument_kind = match expected_ref.skip_binder().self_ty().kind() { + ty::Closure(..) => "closure", + ty::Generator(..) => "generator", _ => "function", }; let mut err = struct_span_err!( @@ -1368,7 +1360,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_suggestion( span, "use the fully qualified path to an implementation", - format!("::{}", self.tcx.def_path_str(trait_ref), assoc_item.ident), + format!("::{}", self.tcx.def_path_str(trait_ref), assoc_item.name), Applicability::HasPlaceholders, ); } @@ -1456,7 +1448,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // 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.kind().skip_binder() { - ty::PredicateKind::Trait(p) => (Some(p.trait_ref), Some(p.self_ty())), + ty::PredicateKind::Trait(p) => (Some(p), Some(p.self_ty())), _ => (None, None), }; let mut generator = None; @@ -1474,11 +1466,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::DerivedObligation(derived_obligation) | ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) | ObligationCauseCode::ImplDerivedObligation(derived_obligation) => { - let ty = derived_obligation.parent_trait_ref.skip_binder().self_ty(); + let ty = derived_obligation.parent_trait_pred.skip_binder().self_ty(); debug!( "maybe_note_obligation_cause_for_async_await: \ parent_trait_ref={:?} self_ty.kind={:?}", - derived_obligation.parent_trait_ref, + derived_obligation.parent_trait_pred, ty.kind() ); @@ -1496,7 +1488,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { seen_upvar_tys_infer_tuple = true; } _ if generator.is_none() => { - trait_ref = Some(derived_obligation.parent_trait_ref.skip_binder()); + trait_ref = Some(derived_obligation.parent_trait_pred.skip_binder()); target_ty = Some(ty); } _ => {} @@ -1559,7 +1551,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // `erase_late_bound_regions`. let ty_erased = self.tcx.erase_late_bound_regions(ty); let ty_erased = self.tcx.erase_regions(ty_erased); - let eq = ty::TyS::same_type(ty_erased, target_ty_erased); + let eq = ty_erased == target_ty_erased; debug!( "maybe_note_obligation_cause_for_async_await: ty_erased={:?} \ target_ty_erased={:?} eq={:?}", @@ -1601,7 +1593,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(cause) = typeck_results.generator_interior_types.as_ref().skip_binder().iter().find( |ty::GeneratorInteriorTypeCause { ty, .. }| { - ty_matches(typeck_results.generator_interior_types.rebind(ty)) + ty_matches(typeck_results.generator_interior_types.rebind(*ty)) }, ) { @@ -1652,7 +1644,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { interior_extra_info: Option<(Option, Span, Option, Option)>, inner_generator_body: Option<&hir::Body<'tcx>>, outer_generator: Option, - trait_ref: ty::TraitRef<'tcx>, + trait_pred: ty::TraitPredicate<'tcx>, target_ty: Ty<'tcx>, typeck_results: Option<&ty::TypeckResults<'tcx>>, obligation: &PredicateObligation<'tcx>, @@ -1672,7 +1664,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // not implemented. let hir = self.tcx.hir(); let trait_explanation = if let Some(name @ (sym::Send | sym::Sync)) = - self.tcx.get_diagnostic_name(trait_ref.def_id) + self.tcx.get_diagnostic_name(trait_pred.def_id()) { let (trait_name, trait_verb) = if name == sym::Send { ("`Send`", "sent") } else { ("`Sync`", "shared") }; @@ -1714,7 +1706,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { format!("is not {}", trait_name) } else { - format!("does not implement `{}`", trait_ref.print_only_trait_path()) + format!("does not implement `{}`", trait_pred.print_modifiers_and_trait_path()) }; let mut explain_yield = |interior_span: Span, @@ -1895,6 +1887,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.note_obligation_cause_code( err, &obligation.predicate, + obligation.param_env, next_code.unwrap(), &mut Vec::new(), &mut Default::default(), @@ -1905,8 +1898,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, err: &mut DiagnosticBuilder<'_>, predicate: &T, + param_env: ty::ParamEnv<'tcx>, cause_code: &ObligationCauseCode<'tcx>, - obligated_types: &mut Vec<&ty::TyS<'tcx>>, + obligated_types: &mut Vec>, seen_requirements: &mut FxHashSet, ) where T: fmt::Display, @@ -1934,7 +1928,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::AwaitableExpr(_) | ObligationCauseCode::ForLoopIterator | ObligationCauseCode::QuestionMark - | ObligationCauseCode::LetElse => {} + | ObligationCauseCode::LetElse + | ObligationCauseCode::CheckAssociatedTypeBounds { .. } => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } @@ -2135,7 +2130,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.note("shared static variables must have a type that implements `Sync`"); } ObligationCauseCode::BuiltinDerivedObligation(ref data) => { - let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); let ty = parent_trait_ref.skip_binder().self_ty(); if parent_trait_ref.references_error() { err.cancel(); @@ -2150,7 +2145,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = *data.parent_code { - let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); + let parent_trait_ref = + self.resolve_vars_if_possible(data.parent_trait_pred); let ty = parent_trait_ref.skip_binder().self_ty(); matches!(ty.kind(), ty::Generator(..)) || matches!(ty.kind(), ty::Closure(..)) @@ -2173,13 +2169,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligated_types.push(ty); - let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); + let parent_predicate = parent_trait_ref.to_predicate(tcx); if !self.is_recursive_obligation(obligated_types, &data.parent_code) { // #74711: avoid a stack overflow ensure_sufficient_stack(|| { self.note_obligation_cause_code( err, &parent_predicate, + param_env, &data.parent_code, obligated_types, seen_requirements, @@ -2190,6 +2187,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.note_obligation_cause_code( err, &parent_predicate, + param_env, &cause_code.peel_derives(), obligated_types, seen_requirements, @@ -2198,17 +2196,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } ObligationCauseCode::ImplDerivedObligation(ref data) => { - let mut parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); - let parent_def_id = parent_trait_ref.def_id(); + let mut parent_trait_pred = self.resolve_vars_if_possible(data.parent_trait_pred); + parent_trait_pred.remap_constness_diag(param_env); + let parent_def_id = parent_trait_pred.def_id(); let msg = format!( "required because of the requirements on the impl of `{}` for `{}`", - parent_trait_ref.print_only_trait_path(), - parent_trait_ref.skip_binder().self_ty() + parent_trait_pred.print_modifiers_and_trait_path(), + parent_trait_pred.skip_binder().self_ty() ); let mut candidates = vec![]; self.tcx.for_each_relevant_impl( parent_def_id, - parent_trait_ref.self_ty().skip_binder(), + parent_trait_pred.self_ty().skip_binder(), |impl_def_id| match self.tcx.hir().get_if_local(impl_def_id) { Some(Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { .. }), @@ -2237,21 +2236,21 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => err.note(&msg), }; - let mut parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); + let mut parent_predicate = parent_trait_pred.to_predicate(tcx); let mut data = data; let mut count = 0; seen_requirements.insert(parent_def_id); while let ObligationCauseCode::ImplDerivedObligation(child) = &*data.parent_code { // Skip redundant recursive obligation notes. See `ui/issue-20413.rs`. - let child_trait_ref = self.resolve_vars_if_possible(child.parent_trait_ref); - let child_def_id = child_trait_ref.def_id(); + let child_trait_pred = self.resolve_vars_if_possible(child.parent_trait_pred); + let child_def_id = child_trait_pred.def_id(); if seen_requirements.insert(child_def_id) { break; } count += 1; data = child; - parent_predicate = child_trait_ref.without_const().to_predicate(tcx); - parent_trait_ref = child_trait_ref; + parent_predicate = child_trait_pred.to_predicate(tcx); + parent_trait_pred = child_trait_pred; } if count > 0 { err.note(&format!( @@ -2261,8 +2260,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { )); err.note(&format!( "required because of the requirements on the impl of `{}` for `{}`", - parent_trait_ref.print_only_trait_path(), - parent_trait_ref.skip_binder().self_ty() + parent_trait_pred.print_modifiers_and_trait_path(), + parent_trait_pred.skip_binder().self_ty() )); } // #74711: avoid a stack overflow @@ -2270,6 +2269,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.note_obligation_cause_code( err, &parent_predicate, + param_env, &data.parent_code, obligated_types, seen_requirements, @@ -2277,13 +2277,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }); } ObligationCauseCode::DerivedObligation(ref data) => { - let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); - let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); + let parent_predicate = parent_trait_ref.to_predicate(tcx); // #74711: avoid a stack overflow ensure_sufficient_stack(|| { self.note_obligation_cause_code( err, &parent_predicate, + param_env, &data.parent_code, obligated_types, seen_requirements, @@ -2301,7 +2302,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { { let in_progress_typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()); - let parent_id = hir.local_def_id(hir.get_parent_item(arg_hir_id)); + let parent_id = hir.get_parent_item(arg_hir_id); let typeck_results: &TypeckResults<'tcx> = match &in_progress_typeck_results { Some(t) if t.hir_owner == parent_id => t, _ => self.tcx.typeck(parent_id), @@ -2322,7 +2323,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(Node::Expr(hir::Expr { kind: hir::ExprKind::Call(hir::Expr { span, .. }, _) - | hir::ExprKind::MethodCall(_, span, ..), + | hir::ExprKind::MethodCall( + hir::PathSegment { ident: Ident { span, .. }, .. }, + .., + ), .. })) = hir.find(call_hir_id) { @@ -2334,6 +2338,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.note_obligation_cause_code( err, predicate, + param_env, &parent_code, obligated_types, seen_requirements, @@ -2424,15 +2429,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, span: Span, ) { debug!( - "suggest_await_before_try: obligation={:?}, span={:?}, trait_ref={:?}, trait_ref_self_ty={:?}", + "suggest_await_before_try: obligation={:?}, span={:?}, trait_pred={:?}, trait_pred_self_ty={:?}", obligation, span, - trait_ref, - trait_ref.self_ty() + trait_pred, + trait_pred.self_ty() ); let body_hir_id = obligation.cause.body_id; let item_id = self.tcx.hir().get_parent_node(body_hir_id); @@ -2442,7 +2447,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); - let self_ty = self.resolve_vars_if_possible(trait_ref.self_ty()); + let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); // Do not check on infer_types to avoid panic in evaluate_obligation. if self_ty.has_infer_types() { @@ -2462,8 +2467,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let projection_ty = ty::ProjectionTy { // `T` substs: self.tcx.mk_substs_trait( - trait_ref.self_ty().skip_binder(), - self.fresh_substs_for_item(span, item_def_id), + trait_pred.self_ty().skip_binder(), + &self.fresh_substs_for_item(span, item_def_id)[1..], ), // `Future::Output` item_def_id, @@ -2487,8 +2492,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); let try_obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, - trait_ref, - normalized_ty, + trait_pred, + normalized_ty.ty().unwrap(), ); debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation); if self.predicate_may_hold(&try_obligation) @@ -2519,12 +2524,6 @@ pub struct ReturnsVisitor<'v> { } impl<'v> Visitor<'v> for ReturnsVisitor<'v> { - type Map = hir::intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::None - } - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { // Visit every expression to detect `return` paths, either through the function's tail // expression or `return` statements. We walk all nodes to find `return` statements, but @@ -2581,12 +2580,6 @@ struct AwaitsVisitor { } impl<'v> Visitor<'v> for AwaitsVisitor { - type Map = hir::intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::None - } - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { if let hir::ExprKind::Yield(_, hir::YieldSource::Await { expr: Some(id) }) = ex.kind { self.awaits.push(id) diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 42e3f0db15..1989184f48 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -200,7 +200,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { debug!(?normalized_ty); - normalized_ty + normalized_ty.ty().unwrap() } fn register_predicate_obligation( @@ -538,7 +538,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { Err(NotConstEvaluatable::MentionsInfer) => { pending_obligation.stalled_on.clear(); pending_obligation.stalled_on.extend( - uv.substs(infcx.tcx) + uv.substs .iter() .filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); @@ -562,7 +562,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { // // Let's just see where this breaks :shrug: if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = - (c1.val, c2.val) + (c1.val(), c2.val()) { if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { return ProcessResult::Changed(vec![]); @@ -572,18 +572,18 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { let stalled_on = &mut pending_obligation.stalled_on; - let mut evaluate = |c: &'tcx Const<'tcx>| { - if let ty::ConstKind::Unevaluated(unevaluated) = c.val { + let mut evaluate = |c: Const<'tcx>| { + if let ty::ConstKind::Unevaluated(unevaluated) = c.val() { match self.selcx.infcx().const_eval_resolve( obligation.param_env, unevaluated, Some(obligation.cause.span), ) { - Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)), + Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty())), Err(ErrorHandled::TooGeneric) => { stalled_on.extend( unevaluated - .substs(tcx) + .substs .iter() .filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); @@ -654,7 +654,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { stalled_on: &mut Vec>, ) -> ProcessResult, FulfillmentErrorCode<'tcx>> { let infcx = self.selcx.infcx(); - if obligation.predicate.is_known_global() { + if obligation.predicate.is_global() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. if infcx.predicate_must_hold_considering_regions(obligation) { @@ -708,7 +708,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { ) -> ProcessResult, FulfillmentErrorCode<'tcx>> { let tcx = self.selcx.tcx(); - if obligation.predicate.is_global(tcx) { + if obligation.predicate.is_global() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. if self.selcx.infcx().predicate_must_hold_considering_regions(obligation) { @@ -756,15 +756,14 @@ fn substs_infer_vars<'a, 'tcx>( selcx: &mut SelectionContext<'a, 'tcx>, substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> impl Iterator> { - let tcx = selcx.tcx(); selcx .infcx() .resolve_vars_if_possible(substs) .skip_binder() // ok because this check doesn't care about regions .iter() .filter(|arg| arg.has_infer_types_or_consts()) - .flat_map(move |arg| { - let mut walker = arg.walk(tcx); + .flat_map(|arg| { + let mut walker = arg.walk(); while let Some(c) = walker.next() { if !c.has_infer_types_or_consts() { walker.visited.remove(&c); diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index a8f26982d2..2927e64f70 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -291,7 +291,7 @@ pub fn normalize_param_env_or_error<'tcx>( // // In any case, in practice, typeck constructs all the // parameter environments once for every fn as it goes, - // and errors will get reported then; so after typeck we + // and errors will get reported then; so outside of type inference we // can be sure that no errors should occur. debug!( @@ -465,7 +465,7 @@ fn subst_and_check_impossible_predicates<'tcx>( debug!("subst_and_check_impossible_predicates(key={:?})", key); let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; - predicates.retain(|predicate| !predicate.definitely_needs_subst(tcx)); + predicates.retain(|predicate| !predicate.needs_subst()); let result = impossible_predicates(tcx, predicates); debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 4e84849bc1..7818053218 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -89,7 +89,7 @@ fn object_safety_violations_for_trait( .filter(|item| item.kind == ty::AssocKind::Fn) .filter_map(|item| { object_safety_violation_for_method(tcx, trait_def_id, &item) - .map(|(code, span)| ObjectSafetyViolation::Method(item.ident.name, code, span)) + .map(|(code, span)| ObjectSafetyViolation::Method(item.name, code, span)) }) .filter(|violation| { if let ObjectSafetyViolation::Method( @@ -125,7 +125,10 @@ fn object_safety_violations_for_trait( tcx.associated_items(trait_def_id) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Const) - .map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)), + .map(|item| { + let ident = item.ident(tcx); + ObjectSafetyViolation::AssocConst(ident.name, ident.span) + }), ); violations.extend( @@ -133,7 +136,10 @@ fn object_safety_violations_for_trait( .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .filter(|item| !tcx.generics_of(item.def_id).params.is_empty()) - .map(|item| ObjectSafetyViolation::GAT(item.ident.name, item.ident.span)), + .map(|item| { + let ident = item.ident(tcx); + ObjectSafetyViolation::GAT(ident.name, ident.span) + }), ); debug!( @@ -274,7 +280,7 @@ fn predicate_references_self<'tcx>( (predicate, sp): (ty::Predicate<'tcx>, Span), ) -> Option { let self_ty = tcx.types.self_param; - let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk(tcx).any(|arg| arg == self_ty.into()); + let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into()); match predicate.kind().skip_binder() { ty::PredicateKind::Trait(ref data) => { // In the case of a trait predicate, we can skip the "self" type. @@ -367,15 +373,15 @@ fn object_safety_violation_for_method( (MethodViolationCode::ReferencesSelfInput(arg), Some(node)) => node .fn_decl() .and_then(|decl| decl.inputs.get(arg + 1)) - .map_or(method.ident.span, |arg| arg.span), + .map_or(method.ident(tcx).span, |arg| arg.span), (MethodViolationCode::UndispatchableReceiver, Some(node)) => node .fn_decl() .and_then(|decl| decl.inputs.get(0)) - .map_or(method.ident.span, |arg| arg.span), + .map_or(method.ident(tcx).span, |arg| arg.span), (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { - node.fn_decl().map_or(method.ident.span, |decl| decl.output.span()) + node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span()) } - _ => method.ident.span, + _ => method.ident(tcx).span, }; (v, span) }) @@ -404,10 +410,10 @@ fn virtual_call_violation_for_method<'tcx>( ); // Get the span pointing at where the `self` receiver should be. let sm = tcx.sess.source_map(); - let self_span = method.ident.span.to(tcx + let self_span = method.ident(tcx).span.to(tcx .hir() .span_if_local(method.def_id) - .unwrap_or_else(|| sm.next_point(method.ident.span)) + .unwrap_or_else(|| sm.next_point(method.ident(tcx).span)) .shrink_to_hi()); let self_span = sm.span_through_char(self_span, '(').shrink_to_hi(); return Some(MethodViolationCode::StaticMethod( @@ -571,7 +577,7 @@ fn object_ty_for_trait<'tcx>( // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`. super_trait_ref.map_bound(|super_trait_ref| { ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - ty: tcx.mk_projection(item.def_id, super_trait_ref.substs), + term: tcx.mk_projection(item.def_id, super_trait_ref.substs).into(), item_def_id: item.def_id, substs: super_trait_ref.substs, }) @@ -768,9 +774,6 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> { type BreakTy = (); - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match t.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index 4840995275..b05dbbe898 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -19,6 +19,7 @@ pub struct OnUnimplementedDirective { pub label: Option, pub note: Option, pub enclosing_scope: Option, + pub append_const_msg: Option>, } #[derive(Default)] @@ -27,6 +28,11 @@ pub struct OnUnimplementedNote { pub label: Option, pub note: Option, pub enclosing_scope: Option, + /// Append a message for `~const Trait` errors. `None` means not requested and + /// should fallback to a generic message, `Some(None)` suggests using the default + /// appended message, `Some(Some(s))` suggests use the `s` message instead of the + /// default one.. + pub append_const_msg: Option>, } fn parse_error( @@ -56,6 +62,10 @@ impl<'tcx> OnUnimplementedDirective { let mut errored = false; let mut item_iter = items.iter(); + let parse_value = |value_str| { + OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some) + }; + let condition = if is_root { None } else { @@ -80,7 +90,14 @@ impl<'tcx> OnUnimplementedDirective { None, ) })?; - attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true); + attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |item| { + if let Some(symbol) = item.value_str() { + if parse_value(symbol).is_err() { + errored = true; + } + } + true + }); Some(cond.clone()) }; @@ -89,10 +106,7 @@ impl<'tcx> OnUnimplementedDirective { let mut note = None; let mut enclosing_scope = None; let mut subcommands = vec![]; - - let parse_value = |value_str| { - OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some) - }; + let mut append_const_msg = None; for item in item_iter { if item.has_name(sym::message) && message.is_none() { @@ -131,6 +145,14 @@ impl<'tcx> OnUnimplementedDirective { } continue; } + } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() { + if let Some(msg) = item.value_str() { + append_const_msg = Some(Some(msg)); + continue; + } else if item.is_word() { + append_const_msg = Some(None); + continue; + } } // nothing found @@ -153,6 +175,7 @@ impl<'tcx> OnUnimplementedDirective { label, note, enclosing_scope, + append_const_msg, }) } } @@ -183,6 +206,7 @@ impl<'tcx> OnUnimplementedDirective { )?), note: None, enclosing_scope: None, + append_const_msg: None, })) } else { return Err(ErrorReported); @@ -201,8 +225,12 @@ impl<'tcx> OnUnimplementedDirective { let mut label = None; let mut note = None; let mut enclosing_scope = None; + let mut append_const_msg = None; info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); + let options_map: FxHashMap = + options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect(); + for command in self.subcommands.iter().chain(Some(self)).rev() { if let Some(ref condition) = command.condition { if !attr::eval_condition( @@ -211,7 +239,11 @@ impl<'tcx> OnUnimplementedDirective { Some(tcx.features()), &mut |c| { c.ident().map_or(false, |ident| { - options.contains(&(ident.name, c.value_str().map(|s| s.to_string()))) + let value = c.value_str().map(|s| { + OnUnimplementedFormatString(s).format(tcx, trait_ref, &options_map) + }); + + options.contains(&(ident.name, value)) }) }, ) { @@ -235,15 +267,16 @@ impl<'tcx> OnUnimplementedDirective { if let Some(ref enclosing_scope_) = command.enclosing_scope { enclosing_scope = Some(enclosing_scope_.clone()); } + + append_const_msg = command.append_const_msg.clone(); } - let options: FxHashMap = - options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect(); OnUnimplementedNote { - label: label.map(|l| l.format(tcx, trait_ref, &options)), - message: message.map(|m| m.format(tcx, trait_ref, &options)), - note: note.map(|n| n.format(tcx, trait_ref, &options)), - enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)), + label: label.map(|l| l.format(tcx, trait_ref, &options_map)), + message: message.map(|m| m.format(tcx, trait_ref, &options_map)), + note: note.map(|n| n.format(tcx, trait_ref, &options_map)), + enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), + append_const_msg, } } } @@ -276,17 +309,23 @@ impl<'tcx> OnUnimplementedFormatString { Piece::String(_) => (), // Normal string, no need to check it Piece::NextArgument(a) => match a.position { // `{Self}` is allowed - Position::ArgumentNamed(s) if s == kw::SelfUpper => (), + Position::ArgumentNamed(s, _) if s == kw::SelfUpper => (), // `{ThisTraitsName}` is allowed - Position::ArgumentNamed(s) if s == name => (), + Position::ArgumentNamed(s, _) if s == name => (), // `{from_method}` is allowed - Position::ArgumentNamed(s) if s == sym::from_method => (), + Position::ArgumentNamed(s, _) if s == sym::from_method => (), // `{from_desugaring}` is allowed - Position::ArgumentNamed(s) if s == sym::from_desugaring => (), + Position::ArgumentNamed(s, _) if s == sym::from_desugaring => (), // `{ItemContext}` is allowed - Position::ArgumentNamed(s) if s == sym::ItemContext => (), + Position::ArgumentNamed(s, _) if s == sym::ItemContext => (), + // `{integral}` and `{integer}` and `{float}` are allowed + Position::ArgumentNamed(s, _) + if s == sym::integral || s == sym::integer_ || s == sym::float => + { + () + } // So is `{A}` if A is a type parameter - Position::ArgumentNamed(s) => { + Position::ArgumentNamed(s, _) => { match generics.params.iter().find(|param| param.name == s) { Some(_) => (), None => { @@ -353,7 +392,7 @@ impl<'tcx> OnUnimplementedFormatString { .map(|p| match p { Piece::String(s) => s, Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => match generic_map.get(&s) { + Position::ArgumentNamed(s, _) => match generic_map.get(&s) { Some(val) => val, None if s == name => &trait_str, None => { @@ -364,6 +403,12 @@ impl<'tcx> OnUnimplementedFormatString { &empty_string } else if s == sym::ItemContext { &item_context + } else if s == sym::integral { + "{integral}" + } else if s == sym::integer_ { + "{integer}" + } else if s == sym::float { + "{float}" } else { bug!( "broken on_unimplemented {:?} for {:?}: \ diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 23f615a961..dba24fb2f3 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -19,15 +19,17 @@ use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorReported; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; use std::collections::BTreeMap; @@ -44,7 +46,7 @@ pub(super) struct InProgress; /// When attempting to resolve `::Name` ... #[derive(Debug)] -pub enum ProjectionTyError<'tcx> { +pub enum ProjectionError<'tcx> { /// ...we found multiple sources of information and couldn't resolve the ambiguity. TooManyCandidates, @@ -53,7 +55,7 @@ pub enum ProjectionTyError<'tcx> { } #[derive(PartialEq, Eq, Debug)] -enum ProjectionTyCandidate<'tcx> { +enum ProjectionCandidate<'tcx> { /// From a where-clause in the env or object type ParamEnv(ty::PolyProjectionPredicate<'tcx>), @@ -67,28 +69,28 @@ enum ProjectionTyCandidate<'tcx> { Select(Selection<'tcx>), } -enum ProjectionTyCandidateSet<'tcx> { +enum ProjectionCandidateSet<'tcx> { None, - Single(ProjectionTyCandidate<'tcx>), + Single(ProjectionCandidate<'tcx>), Ambiguous, Error(SelectionError<'tcx>), } -impl<'tcx> ProjectionTyCandidateSet<'tcx> { +impl<'tcx> ProjectionCandidateSet<'tcx> { fn mark_ambiguous(&mut self) { - *self = ProjectionTyCandidateSet::Ambiguous; + *self = ProjectionCandidateSet::Ambiguous; } fn mark_error(&mut self, err: SelectionError<'tcx>) { - *self = ProjectionTyCandidateSet::Error(err); + *self = ProjectionCandidateSet::Error(err); } // Returns true if the push was successful, or false if the candidate // was discarded -- this could be because of ambiguity, or because // a higher-priority candidate is already there. - fn push_candidate(&mut self, candidate: ProjectionTyCandidate<'tcx>) -> bool { - use self::ProjectionTyCandidate::*; - use self::ProjectionTyCandidateSet::*; + fn push_candidate(&mut self, candidate: ProjectionCandidate<'tcx>) -> bool { + use self::ProjectionCandidate::*; + use self::ProjectionCandidateSet::*; // This wacky variable is just used to try and // make code readable and avoid confusing paths. @@ -196,7 +198,9 @@ fn project_and_unify_type<'cx, 'tcx>( debug!(?obligation, "project_and_unify_type"); let mut obligations = vec![]; - let normalized_ty = match opt_normalize_projection_type( + + let infcx = selcx.infcx(); + let normalized = match opt_normalize_projection_type( selcx, obligation.param_env, obligation.predicate.projection_ty, @@ -208,13 +212,10 @@ fn project_and_unify_type<'cx, 'tcx>( Ok(None) => return Ok(Ok(None)), Err(InProgress) => return Ok(Err(InProgress)), }; - - debug!(?normalized_ty, ?obligations, "project_and_unify_type result"); - - let infcx = selcx.infcx(); + debug!(?normalized, ?obligations, "project_and_unify_type result"); match infcx .at(&obligation.cause, obligation.param_env) - .eq(normalized_ty, obligation.predicate.ty) + .eq(normalized, obligation.predicate.term) { Ok(InferOk { obligations: inferred_obligations, value: () }) => { obligations.extend(inferred_obligations); @@ -392,7 +393,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { // severe performance implications for large opaque types with // late-bound regions. See `issue-88862` benchmark. ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { - // Only normalize `impl Trait` after type-checking, usually in codegen. + // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.param_env.reveal() { Reveal::UserFacing => ty.super_fold_with(self), @@ -442,7 +443,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { obligations.len = ?self.obligations.len(), "AssocTypeNormalizer: normalized type" ); - normalized_ty + normalized_ty.ty().unwrap() } ty::Projection(data) => { @@ -472,6 +473,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { ) .ok() .flatten() + .map(|term| term.ty().unwrap()) .map(|normalized_ty| { PlaceholderReplacer::replace_placeholders( infcx, @@ -498,7 +500,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { } } - fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { if self.selcx.tcx().lazy_normalization() { constant } else { @@ -621,24 +623,24 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { } } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - match *ct { - ty::Const { val: ty::ConstKind::Bound(debruijn, _), ty: _ } + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.val() { + ty::ConstKind::Bound(debruijn, _) if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { bug!("Bound vars outside of `self.universe_indices`"); } - ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty } - if debruijn >= self.current_index => - { + ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); let p = ty::PlaceholderConst { universe, - name: ty::BoundConst { var: bound_const, ty }, + name: ty::BoundConst { var: bound_const, ty: ct.ty() }, }; self.mapped_consts.insert(p, bound_const); - self.infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Placeholder(p), ty }) + self.infcx + .tcx + .mk_const(ty::ConstS { val: ty::ConstKind::Placeholder(p), ty: ct.ty() }) } _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self), _ => ct, @@ -696,7 +698,7 @@ impl<'tcx> TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { } fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { - let r1 = match r0 { + let r1 = match *r0 { ty::ReVar(_) => self .infcx .inner @@ -757,8 +759,8 @@ impl<'tcx> TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { } } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { val: ty::ConstKind::Placeholder(p), ty } = *ct { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Placeholder(p) = ct.val() { let replace_var = self.mapped_consts.get(&p); match replace_var { Some(replace_var) => { @@ -770,8 +772,10 @@ impl<'tcx> TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { let db = ty::DebruijnIndex::from_usize( self.universe_indices.len() - index + self.current_index.as_usize() - 1, ); - self.tcx() - .mk_const(ty::Const { val: ty::ConstKind::Bound(db, *replace_var), ty }) + self.tcx().mk_const(ty::ConstS { + val: ty::ConstKind::Bound(db, *replace_var), + ty: ct.ty(), + }) } None => ct, } @@ -794,7 +798,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( cause: ObligationCause<'tcx>, depth: usize, obligations: &mut Vec>, -) -> Ty<'tcx> { +) -> Term<'tcx> { opt_normalize_projection_type( selcx, param_env, @@ -810,7 +814,10 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( // and a deferred predicate to resolve this when more type // information is available. - selcx.infcx().infer_projection(param_env, projection_ty, cause, depth + 1, obligations) + selcx + .infcx() + .infer_projection(param_env, projection_ty, cause, depth + 1, obligations) + .into() }) } @@ -832,7 +839,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( cause: ObligationCause<'tcx>, depth: usize, obligations: &mut Vec>, -) -> Result>, InProgress> { +) -> Result>, InProgress> { let infcx = selcx.infcx(); // Don't use the projection cache in intercrate mode - // the `infcx` may be re-used between intercrate in non-intercrate @@ -908,15 +915,15 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( debug!("opt_normalize_projection_type: found error"); let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); - return Ok(Some(result.value)); + return Ok(Some(result.value.into())); } } let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty); - match project_type(selcx, &obligation) { - Ok(ProjectedTy::Progress(Progress { - ty: projected_ty, + match project(selcx, &obligation) { + Ok(Projected::Progress(Progress { + term: projected_term, obligations: mut projected_obligations, })) => { // if projection succeeded, then what we get out of this @@ -924,10 +931,9 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // an impl, where-clause etc) and hence we must // re-normalize it - let projected_ty = selcx.infcx().resolve_vars_if_possible(projected_ty); - debug!(?projected_ty, ?depth, ?projected_obligations); + let projected_term = selcx.infcx().resolve_vars_if_possible(projected_term); - let mut result = if projected_ty.has_projections() { + let mut result = if projected_term.has_projections() { let mut normalizer = AssocTypeNormalizer::new( selcx, param_env, @@ -935,13 +941,11 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( depth + 1, &mut projected_obligations, ); - let normalized_ty = normalizer.fold(projected_ty); - - debug!(?normalized_ty, ?depth); + let normalized_ty = normalizer.fold(projected_term); Normalized { value: normalized_ty, obligations: projected_obligations } } else { - Normalized { value: projected_ty, obligations: projected_obligations } + Normalized { value: projected_term, obligations: projected_obligations } }; let mut deduped: SsoHashSet<_> = Default::default(); @@ -953,28 +957,27 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( }); if use_cache { - infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); + infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); } obligations.extend(result.obligations); Ok(Some(result.value)) } - Ok(ProjectedTy::NoProgress(projected_ty)) => { - debug!(?projected_ty, "opt_normalize_projection_type: no progress"); + Ok(Projected::NoProgress(projected_ty)) => { let result = Normalized { value: projected_ty, obligations: vec![] }; if use_cache { - infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); + infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); } // No need to extend `obligations`. Ok(Some(result.value)) } - Err(ProjectionTyError::TooManyCandidates) => { + Err(ProjectionError::TooManyCandidates) => { debug!("opt_normalize_projection_type: too many candidates"); if use_cache { infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); } Ok(None) } - Err(ProjectionTyError::TraitSelectionError(_)) => { + Err(ProjectionError::TraitSelectionError(_)) => { debug!("opt_normalize_projection_type: ERROR"); // if we got an error processing the `T as Trait` part, // just return `ty::err` but add the obligation `T : @@ -986,7 +989,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( } let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); - Ok(Some(result.value)) + Ok(Some(result.value.into())) } } } @@ -1033,30 +1036,22 @@ fn normalize_to_error<'a, 'tcx>( Normalized { value: new_value, obligations: vec![trait_obligation] } } -enum ProjectedTy<'tcx> { +enum Projected<'tcx> { Progress(Progress<'tcx>), - NoProgress(Ty<'tcx>), + NoProgress(ty::Term<'tcx>), } struct Progress<'tcx> { - ty: Ty<'tcx>, + term: ty::Term<'tcx>, obligations: Vec>, } impl<'tcx> Progress<'tcx> { fn error(tcx: TyCtxt<'tcx>) -> Self { - Progress { ty: tcx.ty_error(), obligations: vec![] } + Progress { term: tcx.ty_error().into(), obligations: vec![] } } fn with_addl_obligations(mut self, mut obligations: Vec>) -> Self { - debug!( - self.obligations.len = ?self.obligations.len(), - obligations.len = obligations.len(), - "with_addl_obligations" - ); - - debug!(?self.obligations, ?obligations, "with_addl_obligations"); - self.obligations.append(&mut obligations); self } @@ -1067,22 +1062,21 @@ impl<'tcx> Progress<'tcx> { /// IMPORTANT: /// - `obligation` must be fully normalized #[tracing::instrument(level = "info", skip(selcx))] -fn project_type<'cx, 'tcx>( +fn project<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, -) -> Result, ProjectionTyError<'tcx>> { +) -> Result, ProjectionError<'tcx>> { if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) { - debug!("project: overflow!"); // This should really be an immediate error, but some existing code // relies on being able to recover from this. - return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow)); + return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow)); } if obligation.predicate.references_error() { - return Ok(ProjectedTy::Progress(Progress::error(selcx.tcx()))); + return Ok(Projected::Progress(Progress::error(selcx.tcx()))); } - let mut candidates = ProjectionTyCandidateSet::None; + let mut candidates = ProjectionCandidateSet::None; // Make sure that the following procedures are kept in order. ParamEnv // needs to be first because it has highest priority, and Select checks @@ -1093,7 +1087,7 @@ fn project_type<'cx, 'tcx>( assemble_candidates_from_object_ty(selcx, obligation, &mut candidates); - if let ProjectionTyCandidateSet::Single(ProjectionTyCandidate::Object(_)) = candidates { + if let ProjectionCandidateSet::Single(ProjectionCandidate::Object(_)) = candidates { // Avoid normalization cycle from selection (see // `assemble_candidates_from_object_ty`). // FIXME(lazy_normalization): Lazy normalization should save us from @@ -1103,19 +1097,22 @@ fn project_type<'cx, 'tcx>( }; match candidates { - ProjectionTyCandidateSet::Single(candidate) => { - Ok(ProjectedTy::Progress(confirm_candidate(selcx, obligation, candidate))) + ProjectionCandidateSet::Single(candidate) => { + Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate))) } - ProjectionTyCandidateSet::None => Ok(ProjectedTy::NoProgress( + ProjectionCandidateSet::None => Ok(Projected::NoProgress( + // FIXME(associated_const_generics): this may need to change in the future? + // need to investigate whether or not this is fine. selcx .tcx() - .mk_projection(obligation.predicate.item_def_id, obligation.predicate.substs), + .mk_projection(obligation.predicate.item_def_id, obligation.predicate.substs) + .into(), )), // Error occurred while trying to processing impls. - ProjectionTyCandidateSet::Error(e) => Err(ProjectionTyError::TraitSelectionError(e)), + ProjectionCandidateSet::Error(e) => Err(ProjectionError::TraitSelectionError(e)), // Inherent ambiguity that prevents us from even enumerating the // candidates. - ProjectionTyCandidateSet::Ambiguous => Err(ProjectionTyError::TooManyCandidates), + ProjectionCandidateSet::Ambiguous => Err(ProjectionError::TooManyCandidates), } } @@ -1125,14 +1122,13 @@ fn project_type<'cx, 'tcx>( fn assemble_candidates_from_param_env<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { - debug!("assemble_candidates_from_param_env(..)"); assemble_candidates_from_predicates( selcx, obligation, candidate_set, - ProjectionTyCandidate::ParamEnv, + ProjectionCandidate::ParamEnv, obligation.param_env.caller_bounds().iter(), false, ); @@ -1151,7 +1147,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>( fn assemble_candidates_from_trait_def<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { debug!("assemble_candidates_from_trait_def(..)"); @@ -1174,10 +1170,10 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( selcx, obligation, candidate_set, - ProjectionTyCandidate::TraitDef, + ProjectionCandidate::TraitDef, bounds.iter(), true, - ) + ); } /// In the case of a trait object like @@ -1192,7 +1188,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( fn assemble_candidates_from_object_ty<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { debug!("assemble_candidates_from_object_ty(..)"); @@ -1219,64 +1215,69 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( selcx, obligation, candidate_set, - ProjectionTyCandidate::Object, + ProjectionCandidate::Object, env_predicates, false, ); } +#[tracing::instrument( + level = "debug", + skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates) +)] fn assemble_candidates_from_predicates<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, - ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, + ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'tcx>, env_predicates: impl Iterator>, potentially_unnormalized_candidates: bool, ) { - debug!(?obligation, "assemble_candidates_from_predicates"); - let infcx = selcx.infcx(); for predicate in env_predicates { - debug!(?predicate); 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; - - let is_match = same_def_id - && infcx.probe(|_| { - selcx.match_projection_projections( - obligation, - data, - potentially_unnormalized_candidates, - ) - }); - - debug!(?data, ?is_match, ?same_def_id); + if data.projection_def_id() != obligation.predicate.item_def_id { + continue; + } - if is_match { - candidate_set.push_candidate(ctor(data)); + let is_match = infcx.probe(|_| { + selcx.match_projection_projections( + obligation, + data, + potentially_unnormalized_candidates, + ) + }); - if potentially_unnormalized_candidates - && !obligation.predicate.has_infer_types_or_consts() - { - // HACK: Pick the first trait def candidate for a fully - // inferred predicate. This is to allow duplicates that - // differ only in normalization. - return; + match is_match { + ProjectionMatchesProjection::Yes => { + candidate_set.push_candidate(ctor(data)); + + if potentially_unnormalized_candidates + && !obligation.predicate.has_infer_types_or_consts() + { + // HACK: Pick the first trait def candidate for a fully + // inferred predicate. This is to allow duplicates that + // differ only in normalization. + return; + } + } + ProjectionMatchesProjection::Ambiguous => { + candidate_set.mark_ambiguous(); } + ProjectionMatchesProjection::No => {} } } } } +#[tracing::instrument(level = "debug", skip(selcx, obligation, candidate_set))] fn assemble_candidates_from_impls<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { - debug!("assemble_candidates_from_impls"); - // If we are resolving `>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: let poly_trait_ref = ty::Binder::dummy(obligation.predicate.trait_ref(selcx.tcx())); @@ -1299,10 +1300,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( super::ImplSource::Closure(_) | super::ImplSource::Generator(_) | super::ImplSource::FnPointer(_) - | super::ImplSource::TraitAlias(_) => { - debug!(?impl_source); - true - } + | super::ImplSource::TraitAlias(_) => true, super::ImplSource::UserDefined(impl_data) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in @@ -1327,7 +1325,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // NOTE: This should be kept in sync with the similar code in // `rustc_ty_utils::instance::resolve_associated_item()`. let node_item = - assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id) + assoc_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id) .map_err(|ErrorReported| ())?; if node_item.is_final() { @@ -1400,8 +1398,17 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // Any type with multiple potential metadata types is therefore not eligible. let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - // FIXME: should this normalize? - let tail = selcx.tcx().struct_tail_without_normalization(self_ty); + let tail = selcx.tcx().struct_tail_with_normalize(self_ty, |ty| { + normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + ) + .value + }); + match tail.kind() { ty::Bool | ty::Char @@ -1435,7 +1442,12 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) - | ty::Error(_) => false, + | ty::Error(_) => { + if tail.has_infer_types() { + candidate_set.mark_ambiguous(); + } + false + }, } } super::ImplSource::Param(..) => { @@ -1486,7 +1498,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( }; if eligible { - if candidate_set.push_candidate(ProjectionTyCandidate::Select(impl_source)) { + if candidate_set.push_candidate(ProjectionCandidate::Select(impl_source)) { Ok(()) } else { Err(()) @@ -1500,30 +1512,32 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( fn confirm_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - candidate: ProjectionTyCandidate<'tcx>, + candidate: ProjectionCandidate<'tcx>, ) -> Progress<'tcx> { debug!(?obligation, ?candidate, "confirm_candidate"); let mut progress = match candidate { - ProjectionTyCandidate::ParamEnv(poly_projection) - | ProjectionTyCandidate::Object(poly_projection) => { + ProjectionCandidate::ParamEnv(poly_projection) + | ProjectionCandidate::Object(poly_projection) => { confirm_param_env_candidate(selcx, obligation, poly_projection, false) } - ProjectionTyCandidate::TraitDef(poly_projection) => { + ProjectionCandidate::TraitDef(poly_projection) => { confirm_param_env_candidate(selcx, obligation, poly_projection, true) } - ProjectionTyCandidate::Select(impl_source) => { + ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } }; + // When checking for cycle during evaluation, we compare predicates with // "syntactic" equality. Since normalization generally introduces a type // with new region variables, we need to resolve them to existing variables // when possible for this to work. See `auto-trait-projection-recursion.rs` // for a case where this matters. - if progress.ty.has_infer_regions() { - progress.ty = OpportunisticRegionResolver::new(selcx.infcx()).fold_ty(progress.ty); + if progress.term.has_infer_regions() { + progress.term = + progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx())); } progress } @@ -1586,7 +1600,7 @@ fn confirm_generator_candidate<'cx, 'tcx>( gen_sig, ) .map_bound(|(trait_ref, yield_ty, return_ty)| { - let name = tcx.associated_item(obligation.predicate.item_def_id).ident.name; + let name = tcx.associated_item(obligation.predicate.item_def_id).name; let ty = if name == sym::Return { return_ty } else if name == sym::Yield { @@ -1600,7 +1614,7 @@ fn confirm_generator_candidate<'cx, 'tcx>( substs: trait_ref.substs, item_def_id: obligation.predicate.item_def_id, }, - ty, + term: ty.into(), } }); @@ -1626,7 +1640,7 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>( let predicate = ty::ProjectionPredicate { projection_ty: ty::ProjectionTy { substs, item_def_id: discriminant_def_id }, - ty: self_ty.discriminant_ty(tcx), + term: self_ty.discriminant_ty(tcx).into(), }; // We get here from `poly_project_and_unify_type` which replaces bound vars @@ -1640,18 +1654,30 @@ fn confirm_pointee_candidate<'cx, 'tcx>( _: ImplSourcePointeeData, ) -> Progress<'tcx> { let tcx = selcx.tcx(); - let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - let substs = tcx.mk_substs([self_ty.into()].iter()); + let mut obligations = vec![]; + let metadata_ty = self_ty.ptr_metadata_ty(tcx, |ty| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + &mut obligations, + ) + }); + + let substs = tcx.mk_substs([self_ty.into()].iter()); let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); let predicate = ty::ProjectionPredicate { projection_ty: ty::ProjectionTy { substs, item_def_id: metadata_def_id }, - ty: self_ty.ptr_metadata_ty(tcx), + term: metadata_ty.into(), }; confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + .with_addl_obligations(obligations) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( @@ -1720,7 +1746,7 @@ fn confirm_callable_candidate<'cx, 'tcx>( substs: trait_ref.substs, item_def_id: fn_once_output_def_id, }, - ty: ret_type, + term: ret_type.into(), }); confirm_param_env_candidate(selcx, obligation, predicate, true) @@ -1776,7 +1802,9 @@ fn confirm_param_env_candidate<'cx, 'tcx>( Ok(InferOk { value: _, obligations }) => { nested_obligations.extend(obligations); assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations); - Progress { ty: cache_entry.ty, obligations: nested_obligations } + // FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take + // a term instead. + Progress { term: cache_entry.term, obligations: nested_obligations } } Err(e) => { let msg = format!( @@ -1785,7 +1813,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>( ); debug!("confirm_param_env_candidate: {}", msg); let err = infcx.tcx.ty_error_with_message(obligation.cause.span, &msg); - Progress { ty: err, obligations: vec![] } + Progress { term: err.into(), obligations: vec![] } } } } @@ -1802,9 +1830,9 @@ fn confirm_impl_candidate<'cx, 'tcx>( let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let param_env = obligation.param_env; - let assoc_ty = match assoc_ty_def(selcx, impl_def_id, assoc_item_id) { + let assoc_ty = match assoc_def(selcx, impl_def_id, assoc_item_id) { Ok(assoc_ty) => assoc_ty, - Err(ErrorReported) => return Progress { ty: tcx.ty_error(), obligations: nested }, + Err(ErrorReported) => return Progress { term: tcx.ty_error().into(), obligations: nested }, }; if !assoc_ty.item.defaultness.has_value() { @@ -1814,9 +1842,9 @@ fn confirm_impl_candidate<'cx, 'tcx>( // just return Error. debug!( "confirm_impl_candidate: no associated type {:?} for {:?}", - assoc_ty.item.ident, obligation.predicate + assoc_ty.item.name, obligation.predicate ); - return Progress { ty: tcx.ty_error(), obligations: nested }; + return Progress { term: tcx.ty_error().into(), obligations: nested }; } // If we're trying to normalize ` as X>::A` using //`impl X for Vec { type A = Box; }`, then: @@ -1828,15 +1856,25 @@ fn confirm_impl_candidate<'cx, 'tcx>( let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node); let ty = tcx.type_of(assoc_ty.item.def_id); + let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst); + let term: ty::Term<'tcx> = if is_const { + let identity_substs = + crate::traits::InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id); + let did = ty::WithOptConstParam::unknown(assoc_ty.item.def_id); + let val = ty::ConstKind::Unevaluated(ty::Unevaluated::new(did, identity_substs)); + tcx.mk_const(ty::ConstS { ty, val }).into() + } else { + ty.into() + }; if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { let err = tcx.ty_error_with_message( obligation.cause.span, "impl item and trait item have different parameter counts", ); - Progress { ty: err, obligations: nested } + Progress { term: err.into(), obligations: nested } } else { assoc_ty_own_obligations(selcx, obligation, &mut nested); - Progress { ty: ty.subst(tcx, substs), obligations: nested } + Progress { term: term.subst(tcx, substs), obligations: nested } } } @@ -1877,13 +1915,12 @@ fn assoc_ty_own_obligations<'cx, 'tcx>( /// /// Based on the "projection mode", this lookup may in fact only examine the /// topmost impl. See the comments for `Reveal` for more details. -fn assoc_ty_def( +fn assoc_def( selcx: &SelectionContext<'_, '_>, impl_def_id: DefId, - assoc_ty_def_id: DefId, + assoc_def_id: DefId, ) -> Result { let tcx = selcx.tcx(); - let assoc_ty_name = tcx.associated_item(assoc_ty_def_id).ident; let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id; let trait_def = tcx.trait_def(trait_def_id); @@ -1893,21 +1930,18 @@ fn assoc_ty_def( // for the associated item at the given impl. // If there is no such item in that impl, this function will fail with a // cycle error if the specialization graph is currently being built. - let impl_node = specialization_graph::Node::Impl(impl_def_id); - for item in impl_node.items(tcx) { - if matches!(item.kind, ty::AssocKind::Type) - && tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id) - { - return Ok(specialization_graph::LeafDef { - item: *item, - defining_node: impl_node, - finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) }, - }); - } + if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) { + let item = tcx.associated_item(impl_item_id); + let impl_node = specialization_graph::Node::Impl(impl_def_id); + return Ok(specialization_graph::LeafDef { + item: *item, + defining_node: impl_node, + finalizing_node: if item.defaultness.is_default() { None } else { Some(impl_node) }, + }); } let ancestors = trait_def.ancestors(tcx, impl_def_id)?; - if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) { + if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) { Ok(assoc_item) } else { // This is saying that neither the trait nor @@ -1916,7 +1950,11 @@ fn assoc_ty_def( // could only arise through a compiler bug -- // if the user wrote a bad item name, it // should have failed in astconv. - bug!("No associated type `{}` for {}", assoc_ty_name, tcx.def_path_str(impl_def_id)) + bug!( + "No associated type `{}` for {}", + tcx.item_name(assoc_def_id), + tcx.def_path_str(impl_def_id) + ) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index f05582f061..55903a3c36 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -104,7 +104,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Error(_) => true, // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), + ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), // (T1..Tn) and closures have same properties as T1..Tn -- // check if *any* of those are trivial. diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 26bacf787e..6a2bd9ce1e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -77,11 +77,8 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { // The rest of the code is already set up to be lazy about replacing bound vars, // and only when we actually have to normalize. if value.has_escaping_bound_vars() { - let mut max_visitor = MaxEscapingBoundVarVisitor { - tcx: self.infcx.tcx, - outer_index: ty::INNERMOST, - escaping: 0, - }; + let mut max_visitor = + MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 }; value.visit_with(&mut max_visitor); if max_visitor.escaping > 0 { normalizer.universes.extend((0..max_visitor.escaping).map(|_| None)); @@ -104,18 +101,13 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { } /// Visitor to find the maximum escaping bound var -struct MaxEscapingBoundVarVisitor<'tcx> { - tcx: TyCtxt<'tcx>, +struct MaxEscapingBoundVarVisitor { // The index which would count as escaping outer_index: ty::DebruijnIndex, escaping: usize, } -impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - +impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor { fn visit_binder>( &mut self, t: &ty::Binder<'tcx, T>, @@ -148,8 +140,8 @@ impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor<'tcx> { ControlFlow::CONTINUE } - fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> ControlFlow { - match ct.val { + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow { + match ct.val() { ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => { self.escaping = self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize()); @@ -196,7 +188,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { } if let Some(ty) = self.cache.get(&ty) { - return Ok(ty); + return Ok(*ty); } // See note in `rustc_trait_selection::traits::project` about why we @@ -208,7 +200,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { // severe performance implications for large opaque types with // late-bound regions. See `issue-88862` benchmark. ty::Opaque(def_id, substs) if !substs.has_escaping_bound_vars() => { - // Only normalize `impl Trait` after type-checking, usually in codegen. + // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.param_env.reveal() { Reveal::UserFacing => ty.try_super_fold_with(self), @@ -332,8 +324,8 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { fn try_fold_const( &mut self, - constant: &'tcx ty::Const<'tcx>, - ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { + constant: ty::Const<'tcx>, + ) -> Result, Self::Error> { let constant = constant.try_super_fold_with(self)?; Ok(constant.eval(self.infcx.tcx, self.param_env)) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 12ca3faeb3..d662f61e2c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -4,7 +4,9 @@ use crate::infer::canonical::{ use crate::infer::{InferCtxt, InferOk}; use crate::traits::query::Fallible; use crate::traits::ObligationCause; -use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::canonical::{Canonical, Certainty}; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::PredicateObligations; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use std::fmt; @@ -17,7 +19,6 @@ pub mod implied_outlives_bounds; pub mod normalize; pub mod outlives; pub mod prove_predicate; -use self::prove_predicate::ProvePredicate; pub mod subtype; pub use rustc_middle::traits::query::type_op::*; @@ -80,9 +81,14 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx { query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'_, 'tcx>, output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, - ) -> Fallible<(Self::QueryResponse, Option>>)> { + ) -> Fallible<( + Self::QueryResponse, + Option>>, + PredicateObligations<'tcx>, + Certainty, + )> { if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { - return Ok((result, None)); + return Ok((result, None, vec![], Certainty::Proven)); } // FIXME(#33684) -- We need to use @@ -104,20 +110,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx { output_query_region_constraints, )?; - // Typically, instantiating NLL query results does not - // create obligations. However, in some cases there - // are unresolved type variables, and unify them *can* - // create obligations. In that case, we have to go - // fulfill them. We do this via a (recursive) query. - for obligation in obligations { - let ((), _) = ProvePredicate::fully_perform_into( - obligation.param_env.and(ProvePredicate::new(obligation.predicate)), - infcx, - output_query_region_constraints, - )?; - } - - Ok((value, Some(canonical_self))) + Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty)) } } @@ -129,9 +122,39 @@ where fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible> { let mut region_constraints = QueryRegionConstraints::default(); - let (output, canonicalized_query) = + let (output, canonicalized_query, mut obligations, _) = Q::fully_perform_into(self, infcx, &mut region_constraints)?; + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + while !obligations.is_empty() { + trace!("{:#?}", obligations); + let mut progress = false; + for obligation in std::mem::take(&mut obligations) { + let obligation = infcx.resolve_vars_if_possible(obligation); + match ProvePredicate::fully_perform_into( + obligation.param_env.and(ProvePredicate::new(obligation.predicate)), + infcx, + &mut region_constraints, + ) { + Ok(((), _, new, certainty)) => { + obligations.extend(new); + progress = true; + if let Certainty::Ambiguous = certainty { + obligations.push(obligation); + } + } + Err(_) => obligations.push(obligation), + } + } + if !progress { + return Err(NoSolution); + } + } + // Promote the final query-region-constraints into a // (optional) ref-counted vector: let region_constraints = diff --git a/compiler/rustc_trait_selection/src/traits/relationships.rs b/compiler/rustc_trait_selection/src/traits/relationships.rs index e0098cc92d..aea44841b8 100644 --- a/compiler/rustc_trait_selection/src/traits/relationships.rs +++ b/compiler/rustc_trait_selection/src/traits/relationships.rs @@ -62,7 +62,7 @@ pub(crate) fn update<'tcx, T>( if let ty::PredicateKind::Projection(predicate) = obligation.predicate.kind().skip_binder() { // If the projection predicate (Foo::Bar == X) has X as a non-TyVid, // we need to make it into one. - if let Some(vid) = predicate.ty.ty_vid() { + if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) { debug!("relationship: {:?}.output = true", vid); engine.relationships().entry(vid).or_default().output = true; } 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 017f47d435..db86041f61 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -173,6 +173,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let needs_infer = stack.obligation.predicate.has_infer_types_or_consts(); + let sized_predicate = self.tcx().lang_items().sized_trait() + == Some(stack.obligation.predicate.skip_binder().def_id()); + // If there are STILL multiple candidates, we can further // reduce the list by dropping duplicates -- including // resolving specializations. @@ -181,6 +184,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { while i < candidates.len() { let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { self.candidate_should_be_dropped_in_favor_of( + sized_predicate, &candidates[i], &candidates[j], needs_infer, @@ -301,15 +305,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else if lang_items.unsize_trait() == Some(def_id) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); } else if lang_items.drop_trait() == Some(def_id) - && obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst + && obligation.predicate.is_const_if_const() { - if obligation.param_env.constness() == hir::Constness::Const { - self.assemble_const_drop_candidates(obligation, stack, &mut candidates)?; - } else { - debug!("passing ~const Drop bound; in non-const context"); - // `~const Drop` when we are not in a const context has no effect. - candidates.vec.push(ConstDropCandidate) - } + self.assemble_const_drop_candidates(obligation, &mut candidates); } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -338,13 +336,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(candidates) } + #[tracing::instrument(level = "debug", skip(self, candidates))] fn assemble_candidates_from_projected_tys( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - debug!(?obligation, "assemble_candidates_from_projected_tys"); - // Before we go into the whole placeholder thing, just // quickly check if the self-type is a projection at all. match obligation.predicate.skip_binder().trait_ref.self_ty().kind() { @@ -369,12 +366,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// supplied to find out whether it is listed among them. /// /// Never affects the inference environment. + #[tracing::instrument(level = "debug", skip(self, stack, candidates))] fn assemble_candidates_from_caller_bounds<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) -> Result<(), SelectionError<'tcx>> { - debug!(?stack.obligation, "assemble_candidates_from_caller_bounds"); + debug!(?stack.obligation); let all_bounds = stack .obligation @@ -876,6 +874,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; } + #[tracing::instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_trait_alias( &mut self, obligation: &TraitObligation<'tcx>, @@ -883,7 +882,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, '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"); + debug!(?self_ty); let def_id = obligation.predicate.def_id(); @@ -894,6 +893,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Assembles the trait which are built-in to the language itself: /// `Copy`, `Clone` and `Sized`. + #[tracing::instrument(level = "debug", skip(self, candidates))] fn assemble_builtin_bound_candidates( &mut self, conditions: BuiltinImplConditions<'tcx>, @@ -901,152 +901,88 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) { match conditions { BuiltinImplConditions::Where(nested) => { - debug!(?nested, "builtin_bound"); candidates .vec .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() }); } BuiltinImplConditions::None => {} BuiltinImplConditions::Ambiguous => { - debug!("assemble_builtin_bound_candidates: ambiguous builtin"); candidates.ambiguous = true; } } } - fn assemble_const_drop_candidates<'a>( + fn assemble_const_drop_candidates( &mut self, obligation: &TraitObligation<'tcx>, - obligation_stack: &TraitObligationStack<'a, 'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - let mut stack: Vec<(Ty<'tcx>, usize)> = vec![(obligation.self_ty().skip_binder(), 0)]; - - while let Some((ty, depth)) = stack.pop() { - let mut noreturn = false; - - self.check_recursion_depth(depth, obligation)?; - let mut new_candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - let mut copy_obligation = - obligation.with(obligation.predicate.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: self.tcx().require_lang_item(hir::LangItem::Copy, None), - substs: self.tcx().mk_substs_trait(ty, &[]), - }, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - })); - copy_obligation.recursion_depth = depth + 1; - self.assemble_candidates_from_impls(©_obligation, &mut new_candidates); - let copy_conditions = self.copy_clone_conditions(©_obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut new_candidates); - let copy_stack = self.push_stack(obligation_stack.list(), ©_obligation); - self.assemble_candidates_from_caller_bounds(©_stack, &mut new_candidates)?; - - let const_drop_obligation = - obligation.with(obligation.predicate.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: self.tcx().require_lang_item(hir::LangItem::Drop, None), - substs: self.tcx().mk_substs_trait(ty, &[]), - }, - constness: ty::BoundConstness::ConstIfConst, - polarity: ty::ImplPolarity::Positive, - })); - - let const_drop_stack = self.push_stack(obligation_stack.list(), &const_drop_obligation); - self.assemble_candidates_from_caller_bounds(&const_drop_stack, &mut new_candidates)?; - - if !new_candidates.vec.is_empty() { - noreturn = true; - } - debug!(?new_candidates.vec, "assemble_const_drop_candidates"); - - match ty.kind() { - ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::FnPtr(_) - | ty::Never - | ty::Ref(..) - | ty::FnDef(..) - | ty::RawPtr(_) - | ty::Bool - | ty::Char - | ty::Str - | ty::Foreign(_) => {} // Do nothing. These types satisfy `const Drop`. - - ty::Adt(def, subst) => { - let mut set = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - self.assemble_candidates_from_impls( - &obligation.with(obligation.predicate.map_bound(|mut pred| { - pred.trait_ref.substs = self.tcx().mk_substs_trait(ty, &[]); - pred - })), - &mut set, - ); - stack.extend(def.all_fields().map(|f| (f.ty(self.tcx(), subst), depth + 1))); - - debug!(?set.vec, "assemble_const_drop_candidates - ty::Adt"); - if set.vec.into_iter().any(|candidate| { - if let SelectionCandidate::ImplCandidate(did) = candidate { - matches!(self.tcx().impl_constness(did), hir::Constness::NotConst) - } else { - false - } - }) { - if !noreturn { - // has non-const Drop - return Ok(()); - } - debug!("not returning"); - } - } - - ty::Array(ty, _) => stack.push((ty, depth + 1)), - - ty::Tuple(_) => stack.extend(ty.tuple_fields().map(|t| (t, depth + 1))), + ) { + // If the predicate is `~const Drop` in a non-const environment, we don't actually need + // to check anything. We'll short-circuit checking any obligations in confirmation, too. + if obligation.param_env.constness() == hir::Constness::NotConst { + candidates.vec.push(ConstDropCandidate(None)); + return; + } - ty::Closure(_, substs) => { - let substs = substs.as_closure(); - let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty()); - stack.push((ty, depth + 1)); - } + let self_ty = self.infcx().shallow_resolve(obligation.self_ty()); + match self_ty.skip_binder().kind() { + ty::Opaque(..) + | ty::Dynamic(..) + | ty::Error(_) + | ty::Bound(..) + | ty::Param(_) + | ty::Placeholder(_) + | ty::Projection(_) => { + // We don't know if these are `~const Drop`, at least + // not structurally... so don't push a candidate. + } - ty::Generator(_, substs, _) => { - let substs = substs.as_generator(); - let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty()); + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Never + | ty::Foreign(_) + | ty::Array(..) + | ty::Slice(_) + | ty::Closure(..) + | ty::Generator(..) + | ty::Tuple(_) + | ty::GeneratorWitness(_) => { + // These are built-in, and cannot have a custom `impl const Drop`. + candidates.vec.push(ConstDropCandidate(None)); + } - stack.push((ty, depth + 1)); - stack.push((substs.witness(), depth + 1)); - } + ty::Adt(..) => { + // Find a custom `impl Drop` impl, if it exists + let relevant_impl = self.tcx().find_map_relevant_impl( + obligation.predicate.def_id(), + obligation.predicate.skip_binder().trait_ref.self_ty(), + Some, + ); - ty::GeneratorWitness(tys) => stack.extend( - self.tcx().erase_late_bound_regions(*tys).iter().map(|t| (t, depth + 1)), - ), - - ty::Slice(ty) => stack.push((ty, depth + 1)), - - ty::Opaque(..) - | ty::Dynamic(..) - | ty::Error(_) - | ty::Bound(..) - | ty::Infer(_) - | ty::Placeholder(_) - | ty::Projection(..) - | ty::Param(..) => { - if !noreturn { - return Ok(()); + if let Some(impl_def_id) = relevant_impl { + // Check that `impl Drop` is actually const, if there is a custom impl + if self.tcx().impl_constness(impl_def_id) == hir::Constness::Const { + candidates.vec.push(ConstDropCandidate(Some(impl_def_id))); } - debug!("not returning"); + } else { + // Otherwise check the ADT like a built-in type (structurally) + candidates.vec.push(ConstDropCandidate(None)); } } - debug!(?stack, "assemble_const_drop_candidates - in loop"); - } - // all types have passed. - candidates.vec.push(ConstDropCandidate); - Ok(()) + ty::Infer(_) => { + candidates.ambiguous = true; + } + } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index b7fc578ea3..84bc7cdff2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -72,15 +72,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // CheckPredicate(&A: Super) // CheckPredicate(A: ~const Super) // <- still const env, failure // ``` - if obligation.param_env.constness() == Constness::Const - && obligation.predicate.skip_binder().constness == ty::BoundConstness::NotConst - { + if obligation.param_env.is_const() && !obligation.predicate.is_const_if_const() { new_obligation = TraitObligation { cause: obligation.cause.clone(), param_env: obligation.param_env.without_const(), ..*obligation }; - obligation = &new_obligation; } @@ -159,7 +156,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSource::TraitUpcasting(data)) } - ConstDropCandidate => Ok(ImplSource::ConstDrop(ImplSourceConstDropData)), + ConstDropCandidate(def_id) => { + let data = self.confirm_const_drop_candidate(obligation, def_id)?; + Ok(ImplSource::ConstDrop(data)) + } } } @@ -206,7 +206,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { })?); if let ty::Projection(..) = placeholder_self_ty.kind() { - for predicate in tcx.predicates_of(def_id).instantiate_own(tcx, substs).predicates { + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs).predicates; + debug!(?predicates, "projection predicates"); + for predicate in predicates { let normalized = normalize_with_depth_to( self, obligation.param_env, @@ -655,7 +657,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("closure candidate for non-closure {:?}", obligation), }; - let obligation_predicate = obligation.predicate.to_poly_trait_ref(); + let obligation_predicate = obligation.predicate; let Normalized { value: obligation_predicate, mut obligations } = ensure_sufficient_stack(|| { normalize_with_depth( @@ -685,7 +687,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations.extend(self.confirm_poly_trait_refs( obligation.cause.clone(), obligation.param_env, - obligation_predicate, + obligation_predicate.to_poly_trait_ref(), trait_ref, )?); @@ -981,7 +983,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Lifetimes aren't allowed to change during unsizing. GenericArgKind::Lifetime(_) => None, - GenericArgKind::Const(ct) => match ct.val { + GenericArgKind::Const(ct) => match ct.val() { ty::ConstKind::Param(p) => Some(p.index), _ => None, }, @@ -997,7 +999,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tail_field_ty = tcx.type_of(tail_field.did); let mut unsizing_params = GrowableBitSet::new_empty(); - for arg in tail_field_ty.walk(tcx) { + for arg in tail_field_ty.walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { unsizing_params.insert(i); } @@ -1006,7 +1008,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 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(tcx) { + for arg in tcx.type_of(field.did).walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { unsizing_params.remove(i); } @@ -1085,4 +1087,128 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSourceBuiltinData { nested }) } + + fn confirm_const_drop_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + impl_def_id: Option, + ) -> Result>, SelectionError<'tcx>> { + // `~const Drop` in a non-const environment is always trivially true, since our type is `Drop` + if obligation.param_env.constness() == Constness::NotConst { + return Ok(ImplSourceConstDropData { nested: vec![] }); + } + + let tcx = self.tcx(); + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + + let mut nested = vec![]; + let cause = obligation.derived_cause(BuiltinDerivedObligation); + + // If we have a custom `impl const Drop`, then + // first check it like a regular impl candidate + if let Some(impl_def_id) = impl_def_id { + nested.extend(self.confirm_impl_candidate(obligation, impl_def_id).nested); + } + + // We want to confirm the ADT's fields if we have an ADT + let mut stack = match *self_ty.skip_binder().kind() { + ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(tcx, substs)).collect(), + _ => vec![self_ty.skip_binder()], + }; + + while let Some(nested_ty) = stack.pop() { + match *nested_ty.kind() { + // We know these types are trivially drop + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Never + | ty::Foreign(_) => {} + + // These types are built-in, so we can fast-track by registering + // nested predicates for their constituient type(s) + ty::Array(ty, _) | ty::Slice(ty) => { + stack.push(ty); + } + ty::Tuple(tys) => { + stack.extend(tys.iter().map(|ty| ty.expect_ty())); + } + ty::Closure(_, substs) => { + stack.push(substs.as_closure().tupled_upvars_ty()); + } + ty::Generator(_, substs, _) => { + let generator = substs.as_generator(); + stack.extend([generator.tupled_upvars_ty(), generator.witness()]); + } + ty::GeneratorWitness(tys) => { + stack.extend(tcx.erase_late_bound_regions(tys).to_vec()); + } + + // If we have a projection type, make sure to normalize it so we replace it + // with a fresh infer variable + ty::Projection(..) => { + self.infcx.commit_unconditionally(|_| { + let predicate = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth + 1, + self_ty + .rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: self.tcx().require_lang_item(LangItem::Drop, None), + substs: self.tcx().mk_substs_trait(nested_ty, &[]), + }, + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + }) + .to_predicate(tcx), + &mut nested, + ); + + nested.push(Obligation::with_depth( + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + )); + }); + } + + // If we have any other type (e.g. an ADT), just register a nested obligation + // since it's either not `const Drop` (and we raise an error during selection), + // or it's an ADT (and we need to check for a custom impl during selection) + _ => { + let predicate = self_ty + .rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: self.tcx().require_lang_item(LangItem::Drop, None), + substs: self.tcx().mk_substs_trait(nested_ty, &[]), + }, + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + }) + .to_predicate(tcx); + + nested.push(Obligation::with_depth( + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + )); + } + } + } + + Ok(ImplSourceConstDropData { nested }) + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index fa88c8ee37..3b69700530 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -36,7 +36,7 @@ use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::abstract_const::NotConstEvaluatable; -use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences}; +use rustc_middle::ty::fast_reject::{self, SimplifyParams}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef}; @@ -201,6 +201,7 @@ struct EvaluatedCandidate<'tcx> { } /// When does the builtin impl for `T: Trait` apply? +#[derive(Debug)] enum BuiltinImplConditions<'tcx> { /// The impl is conditional on `T1, T2, ...: Trait`. Where(ty::Binder<'tcx, Vec>>), @@ -344,7 +345,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } Err(e) => Err(e), Ok(candidate) => { - debug!(?candidate); + debug!(?candidate, "confirmed"); Ok(Some(candidate)) } } @@ -526,7 +527,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // contain the "'static" lifetime (any other lifetime // would either be late-bound or local), so it is guaranteed // to outlive any other lifetime - if pred.0.is_global(self.infcx.tcx) && !pred.0.has_late_bound_regions() { + if pred.0.is_global() && !pred.0.has_late_bound_regions() { Ok(EvaluatedToOk) } else { Ok(EvaluatedToOkModuloRegions) @@ -642,7 +643,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // Let's just see where this breaks :shrug: if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = - (c1.val, c2.val) + (c1.val(), c2.val()) { if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { return Ok(EvaluatedToOk); @@ -650,15 +651,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - let evaluate = |c: &'tcx ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(unevaluated) = c.val { + let evaluate = |c: ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(unevaluated) = c.val() { self.infcx .const_eval_resolve( obligation.param_env, unevaluated, Some(obligation.cause.span), ) - .map(|val| ty::Const::from_value(self.tcx(), val, c.ty)) + .map(|val| ty::Const::from_value(self.tcx(), val, c.ty())) } else { Ok(c) } @@ -711,12 +712,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { mut obligation: TraitObligation<'tcx>, ) -> Result { if !self.intercrate - && obligation.is_global(self.tcx()) - && obligation - .param_env - .caller_bounds() - .iter() - .all(|bound| bound.definitely_needs_subst(self.tcx())) + && obligation.is_global() + && obligation.param_env.caller_bounds().iter().all(|bound| bound.needs_subst()) { // If a param env has no global bounds, global obligations do not // depend on its particular value in order to work, so we can clear @@ -768,14 +765,38 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?result, "CACHE MISS"); self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result); - stack.cache().on_completion(stack.dfn, |fresh_trait_pred, provisional_result| { - self.insert_evaluation_cache( - param_env, - fresh_trait_pred, - dep_node, - provisional_result.max(result), - ); - }); + stack.cache().on_completion( + stack.dfn, + |fresh_trait_pred, provisional_result, provisional_dep_node| { + // Create a new `DepNode` that has dependencies on: + // * The `DepNode` for the original evaluation that resulted in a provisional cache + // entry being crated + // * The `DepNode` for the *current* evaluation, which resulted in us completing + // provisional caches entries and inserting them into the evaluation cache + // + // This ensures that when a query reads this entry from the evaluation cache, + // it will end up (transitively) dependening on all of the incr-comp dependencies + // created during the evaluation of this trait. For example, evaluating a trait + // will usually require us to invoke `type_of(field_def_id)` to determine the + // constituent types, and we want any queries reading from this evaluation + // cache entry to end up with a transitive `type_of(field_def_id`)` dependency. + // + // By using `in_task`, we're also creating an edge from the *current* query + // to the newly-created `combined_dep_node`. This is probably redundant, + // but it's better to add too many dep graph edges than to add too few + // dep graph edges. + let ((), combined_dep_node) = self.in_task(|this| { + this.tcx().dep_graph.read_index(provisional_dep_node); + this.tcx().dep_graph.read_index(dep_node); + }); + self.insert_evaluation_cache( + param_env, + fresh_trait_pred, + combined_dep_node, + provisional_result.max(result), + ); + }, + ); } else { debug!(?result, "PROVISIONAL"); debug!( @@ -784,7 +805,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fresh_trait_pred, stack.depth, reached_depth, ); - stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_pred, result); + stack.cache().insert_provisional( + stack.dfn, + reached_depth, + fresh_trait_pred, + result, + dep_node, + ); } Ok(result) @@ -1146,9 +1173,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(def_id) if tcx.impl_constness(def_id) == hir::Constness::Const => {} // const param - ParamCandidate(trait_pred) - if trait_pred.skip_binder().constness - == ty::BoundConstness::ConstIfConst => {} + ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {} // auto trait impl AutoImplCandidate(..) => {} // generator, this will raise error in other places @@ -1156,7 +1181,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { GeneratorCandidate => {} // FnDef where the function is const FnPointerCandidate { is_const: true } => {} - ConstDropCandidate => {} + ConstDropCandidate(_) => {} _ => { // reject all other types of candidates continue; @@ -1469,12 +1494,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } + /// Return `Yes` if the obligation's predicate type applies to the env_predicate, and + /// `No` if it does not. Return `Ambiguous` in the case that the projection type is a GAT, + /// and applying this env_predicate constrains any of the obligation's GAT substitutions. + /// + /// This behavior is a somewhat of a hack to prevent overconstraining inference variables + /// in cases like #91762. pub(super) fn match_projection_projections( &mut self, obligation: &ProjectionTyObligation<'tcx>, env_predicate: PolyProjectionPredicate<'tcx>, potentially_unnormalized_candidates: bool, - ) -> bool { + ) -> ProjectionMatchesProjection { let mut nested_obligations = Vec::new(); let (infer_predicate, _) = self.infcx.replace_bound_vars_with_fresh_vars( obligation.cause.span, @@ -1496,7 +1527,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { infer_predicate.projection_ty }; - self.infcx + let is_match = self + .infcx .at(&obligation.cause, obligation.param_env) .sup(obligation.predicate, infer_projection) .map_or(false, |InferOk { obligations, value: () }| { @@ -1505,7 +1537,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested_obligations.into_iter().chain(obligations), ) .map_or(false, |res| res.may_apply()) - }) + }); + + if is_match { + let generics = self.tcx().generics_of(obligation.predicate.item_def_id); + // FIXME(generic-associated-types): Addresses aggressive inference in #92917. + // If this type is a GAT, and of the GAT substs resolve to something new, + // that means that we must have newly inferred something about the GAT. + // We should give up in that case. + if !generics.params.is_empty() + && obligation.predicate.substs[generics.parent_count..] + .iter() + .any(|&p| p.has_infer_types_or_consts() && self.infcx.shallow_resolve(p) != p) + { + ProjectionMatchesProjection::Ambiguous + } else { + ProjectionMatchesProjection::Yes + } + } else { + ProjectionMatchesProjection::No + } } /////////////////////////////////////////////////////////////////////////// @@ -1523,6 +1574,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// See the comment for "SelectionCandidate" for more details. fn candidate_should_be_dropped_in_favor_of( &mut self, + sized_predicate: bool, victim: &EvaluatedCandidate<'tcx>, other: &EvaluatedCandidate<'tcx>, needs_infer: bool, @@ -1535,11 +1587,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // the param_env so that it can be given the lowest priority. See // #50825 for the motivation for this. let is_global = |cand: &ty::PolyTraitPredicate<'tcx>| { - cand.is_global(self.infcx.tcx) && !cand.has_late_bound_regions() + cand.is_global() && !cand.has_late_bound_regions() }; // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, - // and `DiscriminantKindCandidate` to anything else. + // `DiscriminantKindCandidate`, and `ConstDropCandidate` to anything else. // // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. @@ -1556,7 +1608,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate | PointeeCandidate - | ConstDropCandidate, + | ConstDropCandidate(_), _, ) => true, ( @@ -1564,7 +1616,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate | PointeeCandidate - | ConstDropCandidate, + | ConstDropCandidate(_), ) => false, (ParamCandidate(other), ParamCandidate(victim)) => { @@ -1594,6 +1646,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Drop otherwise equivalent non-const fn pointer candidates (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true, + // If obligation is a sized predicate or the where-clause bound is + // global, prefer the projection or object candidate. See issue + // #50825 and #89352. + (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { + sized_predicate || is_global(cand) + } + (ParamCandidate(ref cand), ObjectCandidate(_) | ProjectionCandidate(_)) => { + !(sized_predicate || is_global(cand)) + } + // Global bounds from the where clause should be ignored // here (see issue #50825). Otherwise, we have a where // clause so don't go around looking for impls. @@ -1609,15 +1671,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..) - | ObjectCandidate(_) - | ProjectionCandidate(_), + | TraitAliasCandidate(..), ) => !is_global(cand), - (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { - // Prefer these to a global where-clause bound - // (see issue #50825). - is_global(cand) - } ( ImplCandidate(_) | ClosureCandidate @@ -1953,7 +2008,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Generator(_, ref substs, _) => { let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty()); let witness = substs.as_generator().witness(); - t.rebind(vec![ty].into_iter().chain(iter::once(witness)).collect()) + t.rebind([ty].into_iter().chain(iter::once(witness)).collect()) } ty::GeneratorWitness(types) => { @@ -2004,7 +2059,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .skip_binder() // binder moved -\ .iter() .flat_map(|ty| { - let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(ty); // <----/ + let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(*ty); // <----/ self.infcx.commit_unconditionally(|_| { let placeholder_ty = self.infcx.replace_bound_vars_with_placeholders(ty); @@ -2143,14 +2198,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.tcx(), obligation_ty, SimplifyParams::Yes, - StripReferences::No, - ); - let simplified_impl_ty = fast_reject::simplify_type( - self.tcx(), - impl_ty, - SimplifyParams::No, - StripReferences::No, ); + let simplified_impl_ty = + fast_reject::simplify_type(self.tcx(), impl_ty, SimplifyParams::No); simplified_obligation_ty.is_some() && simplified_impl_ty.is_some() @@ -2382,7 +2432,7 @@ impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> { // chain. Ideally, we should have a way to configure this either // by using -Z verbose or just a CLI argument. let derived_cause = DerivedObligationCause { - parent_trait_ref: obligation.predicate.to_poly_trait_ref(), + parent_trait_pred: obligation.predicate, parent_code: obligation.cause.clone_code(), }; let derived_code = variant(derived_cause); @@ -2505,6 +2555,11 @@ struct ProvisionalEvaluation { from_dfn: usize, reached_depth: usize, result: EvaluationResult, + /// The `DepNodeIndex` created for the `evaluate_stack` call for this provisional + /// evaluation. When we create an entry in the evaluation cache using this provisional + /// cache entry (see `on_completion`), we use this `dep_node` to ensure that future reads from + /// the cache will have all of the necessary incr comp dependencies tracked. + dep_node: DepNodeIndex, } impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { @@ -2547,6 +2602,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { reached_depth: usize, fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, result: EvaluationResult, + dep_node: DepNodeIndex, ) { debug!(?from_dfn, ?fresh_trait_pred, ?result, "insert_provisional"); @@ -2572,7 +2628,10 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { } } - map.insert(fresh_trait_pred, ProvisionalEvaluation { from_dfn, reached_depth, result }); + map.insert( + fresh_trait_pred, + ProvisionalEvaluation { from_dfn, reached_depth, result, dep_node }, + ); } /// Invoked when the node with dfn `dfn` does not get a successful @@ -2623,7 +2682,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { fn on_completion( &self, dfn: usize, - mut op: impl FnMut(ty::PolyTraitPredicate<'tcx>, EvaluationResult), + mut op: impl FnMut(ty::PolyTraitPredicate<'tcx>, EvaluationResult, DepNodeIndex), ) { debug!(?dfn, "on_completion"); @@ -2632,7 +2691,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { { debug!(?fresh_trait_pred, ?eval, "on_completion"); - op(fresh_trait_pred, eval.result); + op(fresh_trait_pred, eval.result, eval.dep_node); } } } @@ -2676,3 +2735,9 @@ impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { write!(f, "TraitObligationStack({:?})", self.obligation) } } + +pub enum ProjectionMatchesProjection { + Yes, + Ambiguous, + No, +} diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index ab732f510f..38a6220082 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -117,9 +117,8 @@ pub fn translate_substs<'a, 'tcx>( /// Specialization is determined by the sets of types to which the impls apply; /// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies /// to. +#[instrument(skip(tcx), level = "debug")] pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { - debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id); - // The feature gate should prevent introducing new specializations, but not // taking advantage of upstream ones. let features = tcx.features(); @@ -258,6 +257,7 @@ pub(super) fn specialization_graph_provider( trait_id: DefId, ) -> specialization_graph::Graph { let mut sg = specialization_graph::Graph::new(); + let overlap_mode = specialization_graph::OverlapMode::get(tcx, trait_id); let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect(); @@ -271,7 +271,7 @@ pub(super) fn specialization_graph_provider( for impl_def_id in trait_impls { if let Some(impl_def_id) = impl_def_id.as_local() { // This is where impl overlap checking happens: - let insert_result = sg.insert(tcx, impl_def_id.to_def_id()); + let insert_result = sg.insert(tcx, impl_def_id.to_def_id(), overlap_mode); // Report error if there was one. let (overlap, used_to_be_allowed) = match insert_result { Err(overlap) => (Some(overlap), None), @@ -485,7 +485,7 @@ crate fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option, impl_def_id: DefId) -> Option { tcx: TyCtxt<'tcx>, impl_def_id: DefId, simplified_self: Option, + overlap_mode: OverlapMode, ) -> Result; } @@ -48,12 +49,7 @@ impl ChildrenExt<'_> for Children { /// Insert an impl into this set of children without comparing to any existing impls. fn insert_blindly(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - if let Some(st) = fast_reject::simplify_type( - tcx, - trait_ref.self_ty(), - SimplifyParams::No, - StripReferences::No, - ) { + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), SimplifyParams::No) { debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); self.non_blanket_impls.entry(st).or_default().push(impl_def_id) } else { @@ -68,12 +64,7 @@ impl ChildrenExt<'_> for Children { fn remove_existing(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let vec: &mut Vec; - if let Some(st) = fast_reject::simplify_type( - tcx, - trait_ref.self_ty(), - SimplifyParams::No, - StripReferences::No, - ) { + if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), SimplifyParams::No) { debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); vec = self.non_blanket_impls.get_mut(&st).unwrap(); } else { @@ -92,6 +83,7 @@ impl ChildrenExt<'_> for Children { tcx: TyCtxt<'_>, impl_def_id: DefId, simplified_self: Option, + overlap_mode: OverlapMode, ) -> Result { let mut last_lint = None; let mut replace_children = Vec::new(); @@ -142,6 +134,7 @@ impl ChildrenExt<'_> for Children { possible_sibling, impl_def_id, traits::SkipLeakCheck::default(), + overlap_mode, |_| true, || false, ); @@ -166,6 +159,7 @@ impl ChildrenExt<'_> for Children { possible_sibling, impl_def_id, traits::SkipLeakCheck::Yes, + overlap_mode, |overlap| { if let Some(overlap_kind) = tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) @@ -273,6 +267,7 @@ pub trait GraphExt { &mut self, tcx: TyCtxt<'_>, impl_def_id: DefId, + overlap_mode: OverlapMode, ) -> Result, OverlapError>; /// Insert cached metadata mapping from a child impl back to its parent. @@ -287,6 +282,7 @@ impl GraphExt for Graph { &mut self, tcx: TyCtxt<'_>, impl_def_id: DefId, + overlap_mode: OverlapMode, ) -> Result, OverlapError> { assert!(impl_def_id.is_local()); @@ -316,19 +312,18 @@ impl GraphExt for Graph { let mut parent = trait_def_id; let mut last_lint = None; - let simplified = fast_reject::simplify_type( - tcx, - trait_ref.self_ty(), - SimplifyParams::No, - StripReferences::No, - ); + let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), SimplifyParams::No); // Descend the specialization tree, where `parent` is the current parent node. loop { use self::Inserted::*; - let insert_result = - self.children.entry(parent).or_default().insert(tcx, impl_def_id, simplified)?; + let insert_result = self.children.entry(parent).or_default().insert( + tcx, + impl_def_id, + simplified, + overlap_mode, + )?; match insert_result { BecameNewSibling(opt_lint) => { diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 55feb3c1de..2ed7a8f9cf 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -48,7 +48,6 @@ pub enum NonStructuralMatchTy<'tcx> { /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. pub fn search_for_structural_match_violation<'tcx>( - _id: hir::HirId, span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, @@ -131,9 +130,6 @@ impl<'a, 'tcx> Search<'a, 'tcx> { impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { type BreakTy = NonStructuralMatchTy<'tcx>; - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx()) - } fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { debug!("Search visiting ty: {:?}", ty); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 4bd73ef68a..2dd3b77a73 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -41,7 +41,7 @@ pub fn obligations<'a, 'tcx>( .into() } GenericArgKind::Const(ct) => { - match ct.val { + match ct.val() { ty::ConstKind::Infer(infer) => { let resolved = infcx.shallow_resolve(infer); if resolved == infer { @@ -49,7 +49,9 @@ pub fn obligations<'a, 'tcx>( return None; } - infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Infer(resolved), ty: ct.ty }) + infcx + .tcx + .mk_const(ty::ConstS { val: ty::ConstKind::Infer(resolved), ty: ct.ty() }) } _ => ct, } @@ -116,7 +118,10 @@ pub fn predicate_obligations<'a, 'tcx>( } ty::PredicateKind::Projection(t) => { wf.compute_projection(t.projection_ty); - wf.compute(t.ty.into()); + wf.compute(match t.term { + ty::Term::Ty(ty) => ty.into(), + ty::Term::Const(c) => c.into(), + }) } ty::PredicateKind::WellFormed(arg) => { wf.compute(arg); @@ -132,11 +137,10 @@ pub fn predicate_obligations<'a, 'tcx>( wf.compute(b.into()); } ty::PredicateKind::ConstEvaluatable(uv) => { - let substs = uv.substs(wf.tcx()); - let obligations = wf.nominal_obligations(uv.def.did, substs); + let obligations = wf.nominal_obligations(uv.def.did, uv.substs); wf.out.extend(obligations); - for arg in substs.iter() { + for arg in uv.substs.iter() { wf.compute(arg); } } @@ -196,15 +200,14 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( trait_ref: &ty::TraitRef<'tcx>, item: Option<&hir::Item<'tcx>>, cause: &mut traits::ObligationCause<'tcx>, - pred: &ty::Predicate<'tcx>, - mut trait_assoc_items: impl Iterator, + pred: ty::Predicate<'tcx>, ) { debug!( "extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}", trait_ref, item, cause, pred ); - let items = match item { - Some(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.items, + let (items, impl_def_id) = match item { + Some(hir::Item { kind: hir::ItemKind::Impl(impl_), def_id, .. }) => (impl_.items, *def_id), _ => return, }; let fix_span = @@ -221,12 +224,17 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // projection coming from another associated type. See // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and // `traits-assoc-type-in-supertrait-bad.rs`. - if let ty::Projection(projection_ty) = proj.ty.kind() { - let trait_assoc_item = tcx.associated_item(projection_ty.item_def_id); - if let Some(impl_item_span) = - items.iter().find(|item| item.ident == trait_assoc_item.ident).map(fix_span) + if let Some(ty::Projection(projection_ty)) = proj.term.ty().map(|ty| ty.kind()) { + if let Some(&impl_item_id) = + tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.item_def_id) { - cause.span = impl_item_span; + if let Some(impl_item_span) = items + .iter() + .find(|item| item.id.def_id.to_def_id() == impl_item_id) + .map(fix_span) + { + cause.span = impl_item_span; + } } } } @@ -235,13 +243,16 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // 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); if let ty::Projection(ty::ProjectionTy { item_def_id, .. }) = *pred.self_ty().kind() { - if let Some(impl_item_span) = trait_assoc_items - .find(|i| i.def_id == item_def_id) - .and_then(|trait_assoc_item| { - items.iter().find(|i| i.ident == trait_assoc_item.ident).map(fix_span) - }) + if let Some(&impl_item_id) = + tcx.impl_item_implementor_ids(impl_def_id).get(&item_def_id) { - cause.span = impl_item_span; + if let Some(impl_item_span) = items + .iter() + .find(|item| item.id.def_id.to_def_id() == impl_item_id) + .map(fix_span) + { + cause.span = impl_item_span; + } } } } @@ -297,10 +308,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let extend = |obligation: traits::PredicateObligation<'tcx>| { let mut cause = cause.clone(); - if let Some(parent_trait_ref) = obligation.predicate.to_opt_poly_trait_pred() { + if let Some(parent_trait_pred) = obligation.predicate.to_opt_poly_trait_pred() { let derived_cause = traits::DerivedObligationCause { - // FIXME(fee1-dead): when improving error messages, change this to PolyTraitPredicate - parent_trait_ref: parent_trait_ref.map_bound(|t| t.trait_ref), + parent_trait_pred, parent_code: obligation.cause.clone_code(), }; *cause.make_mut_code() = @@ -311,8 +321,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { trait_ref, item, &mut cause, - &obligation.predicate, - tcx.associated_items(trait_ref.def_id).in_definition_order(), + obligation.predicate, ); traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate) }; @@ -423,7 +432,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// Pushes all the predicates needed to validate that `ty` is WF into `out`. fn compute(&mut self, arg: GenericArg<'tcx>) { - let mut walker = arg.walk(self.tcx()); + let mut walker = arg.walk(); let param_env = self.param_env; let depth = self.recursion_depth; while let Some(arg) = walker.next() { @@ -435,18 +444,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { GenericArgKind::Lifetime(_) => continue, GenericArgKind::Const(constant) => { - match constant.val { + match constant.val() { ty::ConstKind::Unevaluated(uv) => { - assert!(uv.promoted.is_none()); - let substs = uv.substs(self.tcx()); - - let obligations = self.nominal_obligations(uv.def.did, substs); + let obligations = self.nominal_obligations(uv.def.did, uv.substs); self.out.extend(obligations); - let predicate = ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable( - ty::Unevaluated::new(uv.def, substs), - )) - .to_predicate(self.tcx()); + let predicate = + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(uv.shrink())) + .to_predicate(self.tcx()); let cause = self.cause(traits::MiscObligation); self.out.push(traits::Obligation::with_depth( cause, @@ -461,9 +466,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { if resolved != infer { let cause = self.cause(traits::MiscObligation); - let resolved_constant = self.infcx.tcx.mk_const(ty::Const { + let resolved_constant = self.infcx.tcx.mk_const(ty::ConstS { val: ty::ConstKind::Infer(resolved), - ..*constant + ty: constant.ty(), }); self.out.push(traits::Obligation::with_depth( cause, diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml index f22751dc74..25f228c789 100644 --- a/compiler/rustc_traits/Cargo.toml +++ b/compiler/rustc_traits/Cargo.toml @@ -12,9 +12,9 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.75.0" -chalk-engine = "0.75.0" -chalk-solve = "0.75.0" +chalk-ir = "0.76.0" +chalk-engine = "0.76.0" +chalk-solve = "0.76.0" smallvec = { version = "1.6.1", 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 c38680651a..51b66e1bb6 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -20,11 +20,10 @@ use rustc_span::symbol::sym; use std::fmt; use std::sync::Arc; -use crate::chalk::lowering::{self, LowerInto}; +use crate::chalk::lowering::LowerInto; pub struct RustIrDatabase<'tcx> { pub(crate) interner: RustInterner<'tcx>, - pub(crate) reempty_placeholder: ty::Region<'tcx>, } impl fmt::Debug for RustIrDatabase<'_> { @@ -40,12 +39,9 @@ impl<'tcx> RustIrDatabase<'tcx> { bound_vars: SubstsRef<'tcx>, ) -> Vec>> { let predicates = self.interner.tcx.predicates_defined_on(def_id).predicates; - let mut regions_substitutor = - lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder); predicates .iter() .map(|(wc, _)| wc.subst(self.interner.tcx, bound_vars)) - .map(|wc| wc.fold_with(&mut regions_substitutor)) .filter_map(|wc| LowerInto::< Option>> >::lower_into(wc, self.interner)).collect() @@ -287,9 +283,6 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl"); let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars); - let mut regions_substitutor = - lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder); - let trait_ref = trait_ref.fold_with(&mut regions_substitutor); let where_clauses = self.where_clauses_for(def_id, bound_vars); @@ -335,9 +328,6 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t let self_ty = trait_ref.self_ty(); let self_ty = self_ty.subst(self.interner.tcx, bound_vars); - let mut regions_substitutor = - lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder); - 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( @@ -436,23 +426,13 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t ) -> Arc>> { let def_id = associated_ty_id.0; let assoc_item = self.interner.tcx.associated_item(def_id); - let (impl_id, trait_id) = match assoc_item.container { - AssocItemContainer::TraitContainer(def_id) => (def_id, def_id), - AssocItemContainer::ImplContainer(def_id) => { - (def_id, self.interner.tcx.impl_trait_ref(def_id).unwrap().def_id) - } - }; + let impl_id = assoc_item.container.id(); match assoc_item.kind { AssocKind::Type => {} _ => unimplemented!("Not possible??"), } - let trait_item = self - .interner - .tcx - .associated_items(trait_id) - .find_by_name_and_kind(self.interner.tcx, assoc_item.ident, assoc_item.kind, trait_id) - .unwrap(); + let trait_item_id = assoc_item.trait_item_def_id.expect("assoc_ty with no trait version"); let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(self.interner, bound_vars); let ty = self @@ -464,7 +444,7 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t Arc::new(chalk_solve::rust_ir::AssociatedTyValue { impl_id: chalk_ir::ImplId(impl_id), - associated_ty_id: chalk_ir::AssocTypeId(trait_item.def_id), + associated_ty_id: chalk_ir::AssocTypeId(trait_item_id), value: chalk_ir::Binders::new( binders, chalk_solve::rust_ir::AssociatedTyValueBound { ty }, @@ -566,11 +546,11 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t Fn => lang_items.fn_trait(), FnMut => lang_items.fn_mut_trait(), FnOnce => lang_items.fn_once_trait(), + Generator => lang_items.gen_trait(), Unsize => lang_items.unsize_trait(), Unpin => lang_items.unpin_trait(), CoerceUnsized => lang_items.coerce_unsized_trait(), DiscriminantKind => lang_items.discriminant_kind_trait(), - Generator => lang_items.generator_return(), }; def_id.map(chalk_ir::TraitId) } @@ -694,28 +674,18 @@ impl<'tcx> chalk_ir::UnificationDatabase> for RustIrDatabase< 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!(), - }), + variances.iter().map(|v| v.lower_into(self.interner)), ) } fn adt_variance( &self, - def_id: chalk_ir::AdtId>, + adt_id: chalk_ir::AdtId>, ) -> chalk_ir::Variances> { - let variances = self.interner.tcx.variances_of(def_id.0.did); + let variances = self.interner.tcx.variances_of(adt_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!(), - }), + variances.iter().map(|v| v.lower_into(self.interner)), ) } } @@ -741,11 +711,11 @@ fn bound_vars_for_item<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx var: ty::BoundVar::from_usize(substs.len()), kind: ty::BrAnon(substs.len() as u32), }; - tcx.mk_region(ty::RegionKind::ReLateBound(ty::INNERMOST, br)).into() + tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into() } ty::GenericParamDefKind::Const { .. } => tcx - .mk_const(ty::Const { + .mk_const(ty::ConstS { val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), ty: tcx.type_of(param.def_id), }) @@ -765,7 +735,7 @@ fn binders_for<'tcx>( chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) } ty::subst::GenericArgKind::Const(c) => { - chalk_ir::VariableKind::Const(c.ty.lower_into(interner)) + chalk_ir::VariableKind::Const(c.ty().lower_into(interner)) } }), ) diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index cc07bfc500..9d810d0881 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -35,7 +35,7 @@ use rustc_ast::ast; use rustc_middle::traits::{ChalkEnvironmentAndGoal, ChalkRustInterner as RustInterner}; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, Binder, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor}; +use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::def_id::DefId; use chalk_ir::{FnSig, ForeignDefId}; @@ -188,12 +188,18 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predi chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(t)), ), + ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => { + chalk_ir::GoalData::SubtypeGoal(chalk_ir::SubtypeGoal { + a: a.lower_into(interner), + b: b.lower_into(interner), + }) + } + // FIXME(chalk): other predicates // // We can defer this, but ultimately we'll want to express // some of these in terms of chalk operations. ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) => { @@ -226,13 +232,26 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq>> for rustc_middle::ty::ProjectionPredicate<'tcx> { fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::AliasEq> { + // FIXME(associated_const_equality): teach chalk about terms for alias eq. chalk_ir::AliasEq { - ty: self.ty.lower_into(interner), + ty: self.term.ty().unwrap().lower_into(interner), alias: self.projection_ty.lower_into(interner), } } } +/* +// FIXME(...): Where do I add this to Chalk? I can't find it in the rustc repo anywhere. +impl<'tcx> LowerInto<'tcx, chalk_ir::Term>> for rustc_middle::ty::Term<'tcx> { + fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Term> { + match self { + ty::Term::Ty(ty) => ty.lower_into(interner).into(), + ty::Term::Const(c) => c.lower_into(interner).into(), + } + } +} +*/ + impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Ty> { let int = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(i)); @@ -370,7 +389,7 @@ impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty> { TyKind::Array(ty, c) => { let ty = ty.lower_into(interner); let c = c.lower_into(interner); - ty::Array(ty, interner.tcx.mk_const(c)) + ty::Array(ty, c) } TyKind::FnDef(id, substitution) => ty::FnDef(id.0, substitution.lower_into(interner)), TyKind::Closure(closure, substitution) => { @@ -430,30 +449,30 @@ impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty> { impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'tcx> { fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Lifetime> { - use rustc_middle::ty::RegionKind::*; - - match self { - ReEarlyBound(_) => { + match *self { + ty::ReEarlyBound(_) => { panic!("Should have already been substituted."); } - ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + ty::ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(db.as_u32()), br.var.as_usize(), )) .intern(interner), - ReFree(_) => unimplemented!(), - ReStatic => chalk_ir::LifetimeData::Static.intern(interner), - ReVar(_) => unimplemented!(), - RePlaceholder(placeholder_region) => { + ty::ReFree(_) => unimplemented!(), + ty::ReStatic => chalk_ir::LifetimeData::Static.intern(interner), + ty::ReVar(_) => unimplemented!(), + ty::RePlaceholder(placeholder_region) => { chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { ui: chalk_ir::UniverseIndex { counter: placeholder_region.universe.index() }, idx: 0, }) .intern(interner) } - ReEmpty(_) => unimplemented!(), - // FIXME(chalk): need to handle ReErased - ReErased => unimplemented!(), + ty::ReEmpty(ui) => { + chalk_ir::LifetimeData::Empty(chalk_ir::UniverseIndex { counter: ui.index() }) + .intern(interner) + } + ty::ReErased => chalk_ir::LifetimeData::Erased.intern(interner), } } } @@ -461,7 +480,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'t impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime> { fn lower_into(self, interner: RustInterner<'tcx>) -> Region<'tcx> { let kind = match self.data(interner) { - chalk_ir::LifetimeData::BoundVar(var) => ty::RegionKind::ReLateBound( + chalk_ir::LifetimeData::BoundVar(var) => ty::ReLateBound( ty::DebruijnIndex::from_u32(var.debruijn.depth()), ty::BoundRegion { var: ty::BoundVar::from_usize(var.index), @@ -469,18 +488,16 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime unimplemented!(), - chalk_ir::LifetimeData::Placeholder(p) => { - ty::RegionKind::RePlaceholder(ty::Placeholder { - universe: ty::UniverseIndex::from_usize(p.ui.counter), - name: ty::BoundRegionKind::BrAnon(p.idx as u32), - }) - } - chalk_ir::LifetimeData::Static => ty::RegionKind::ReStatic, - chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), + chalk_ir::LifetimeData::Placeholder(p) => ty::RePlaceholder(ty::Placeholder { + universe: ty::UniverseIndex::from_usize(p.ui.counter), + name: ty::BoundRegionKind::BrAnon(p.idx as u32), + }), + chalk_ir::LifetimeData::Static => return interner.tcx.lifetimes.re_static, chalk_ir::LifetimeData::Empty(ui) => { - ty::RegionKind::ReEmpty(ty::UniverseIndex::from_usize(ui.counter)) + ty::ReEmpty(ty::UniverseIndex::from_usize(ui.counter)) } - chalk_ir::LifetimeData::Erased => ty::RegionKind::ReErased, + chalk_ir::LifetimeData::Erased => return interner.tcx.lifetimes.re_erased, + chalk_ir::LifetimeData::Phantom(void, _) => match *void {}, }; interner.tcx.mk_region(kind) } @@ -488,8 +505,8 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime LowerInto<'tcx, chalk_ir::Const>> for ty::Const<'tcx> { fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Const> { - let ty = self.ty.lower_into(interner); - let value = match self.val { + let ty = self.ty().lower_into(interner); + let value = match self.val() { ty::ConstKind::Value(val) => { chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val }) } @@ -515,7 +532,7 @@ impl<'tcx> LowerInto<'tcx, ty::Const<'tcx>> for &chalk_ir::Const unimplemented!(), chalk_ir::ConstValue::Concrete(c) => ty::ConstKind::Value(c.interned), }; - ty::Const { ty, val } + interner.tcx.mk_const(ty::ConstS { ty, val }) } } @@ -551,7 +568,7 @@ impl<'tcx> LowerInto<'tcx, ty::subst::GenericArg<'tcx>> } chalk_ir::GenericArgData::Const(c) => { let c: ty::Const<'tcx> = c.lower_into(interner); - interner.tcx.mk_const(c).into() + c.into() } } } @@ -651,7 +668,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders chalk_ir::Binders::new( @@ -774,6 +792,16 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::Polarity> for ty::ImplPolarity } } } +impl<'tcx> LowerInto<'tcx, chalk_ir::Variance> for ty::Variance { + fn lower_into(self, _interner: RustInterner<'tcx>) -> chalk_ir::Variance { + match self { + ty::Variance::Covariant => chalk_ir::Variance::Covariant, + ty::Variance::Invariant => chalk_ir::Variance::Invariant, + ty::Variance::Contravariant => chalk_ir::Variance::Contravariant, + ty::Variance::Bivariant => unimplemented!(), + } + } +} impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound>> for ty::ProjectionPredicate<'tcx> @@ -787,7 +815,7 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound trait_bound: trait_ref.lower_into(interner), associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.item_def_id), parameters: own_substs.iter().map(|arg| arg.lower_into(interner)).collect(), - value: self.ty.lower_into(interner), + value: self.term.ty().unwrap().lower_into(interner), } } } @@ -806,7 +834,7 @@ crate fn collect_bound_vars<'tcx, T: TypeFoldable<'tcx>>( tcx: TyCtxt<'tcx>, ty: Binder<'tcx, T>, ) -> (T, chalk_ir::VariableKinds>, BTreeMap) { - let mut bound_vars_collector = BoundVarsCollector::new(tcx); + let mut bound_vars_collector = BoundVarsCollector::new(); ty.as_ref().skip_binder().visit_with(&mut bound_vars_collector); let mut parameters = bound_vars_collector.parameters; let named_parameters: BTreeMap = bound_vars_collector @@ -836,16 +864,14 @@ crate fn collect_bound_vars<'tcx, T: TypeFoldable<'tcx>>( } crate struct BoundVarsCollector<'tcx> { - tcx: TyCtxt<'tcx>, binder_index: ty::DebruijnIndex, crate parameters: BTreeMap>>, crate named_parameters: Vec, } impl<'tcx> BoundVarsCollector<'tcx> { - crate fn new(tcx: TyCtxt<'tcx>) -> Self { + crate fn new() -> Self { BoundVarsCollector { - tcx, binder_index: ty::INNERMOST, parameters: BTreeMap::new(), named_parameters: vec![], @@ -854,10 +880,6 @@ impl<'tcx> BoundVarsCollector<'tcx> { } impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - fn visit_binder>( &mut self, t: &Binder<'tcx, T>, @@ -889,8 +911,8 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { } fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow { - match r { - ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind { + match *r { + ty::ReLateBound(index, br) if index == self.binder_index => match br.kind { ty::BoundRegionKind::BrNamed(def_id, _name) => { if !self.named_parameters.iter().any(|d| *d == def_id) { self.named_parameters.push(def_id); @@ -951,12 +973,12 @@ impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { } fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - match r { - ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind { + match *r { + ty::ReLateBound(index, br) if index == self.binder_index => match br.kind { ty::BrNamed(def_id, _name) => match self.named_parameters.get(&def_id) { Some(idx) => { let new_br = ty::BoundRegion { var: br.var, kind: ty::BrAnon(*idx) }; - return self.tcx.mk_region(RegionKind::ReLateBound(*index, new_br)); + return self.tcx.mk_region(ty::ReLateBound(index, new_br)); } None => panic!("Missing `BrNamed`."), }, @@ -1008,10 +1030,6 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { - // FIXME(chalk): currently we convert params to placeholders starting at - // index `0`. To support placeholders, we'll actually need to do a - // first pass to collect placeholders. Then we can insert params after. - ty::Placeholder(_) => unimplemented!(), ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { Some(idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::from_usize(0), @@ -1027,29 +1045,29 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { })) } }, - _ => t.super_fold_with(self), } } fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - match r { - // FIXME(chalk) - jackh726 - this currently isn't hit in any tests. - // This covers any region variables in a goal, right? + match *r { + // FIXME(chalk) - jackh726 - this currently isn't hit in any tests, + // since canonicalization will already change these to canonical + // variables (ty::ReLateBound). ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) { Some(idx) => { let br = ty::BoundRegion { var: ty::BoundVar::from_u32(*idx), kind: ty::BrAnon(*idx), }; - self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br)) + self.tcx.mk_region(ty::ReLateBound(self.binder_index, br)) } None => { let idx = self.named_regions.len() as u32; let br = ty::BoundRegion { var: ty::BoundVar::from_u32(idx), kind: ty::BrAnon(idx) }; self.named_regions.insert(_re.def_id, idx); - self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br)) + self.tcx.mk_region(ty::ReLateBound(self.binder_index, br)) } }, @@ -1058,6 +1076,39 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { } } +crate struct ReverseParamsSubstitutor<'tcx> { + tcx: TyCtxt<'tcx>, + params: rustc_data_structures::fx::FxHashMap, +} + +impl<'tcx> ReverseParamsSubstitutor<'tcx> { + crate fn new( + tcx: TyCtxt<'tcx>, + params: rustc_data_structures::fx::FxHashMap, + ) -> Self { + Self { tcx, params } + } +} + +impl<'tcx> TypeFolder<'tcx> for ReverseParamsSubstitutor<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, name }) => { + match self.params.get(&name.as_usize()) { + Some(param) => self.tcx.mk_ty(ty::Param(*param)), + None => t, + } + } + + _ => t.super_fold_with(self), + } + } +} + /// Used to collect `Placeholder`s. crate struct PlaceholdersCollector { universe_index: ty::UniverseIndex, @@ -1076,11 +1127,6 @@ impl PlaceholdersCollector { } impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { - fn tcx_for_anon_const_substs(&self) -> Option> { - // Anon const substs do not contain placeholders by default. - None - } - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match t.kind() { ty::Placeholder(p) if p.universe == self.universe_index => { @@ -1094,7 +1140,7 @@ impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { } fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow { - match r { + match *r { ty::RePlaceholder(p) if p.universe == self.universe_index => { if let ty::BoundRegionKind::BrAnon(anon) = p.name { self.next_anon_region_placeholder = self.next_anon_region_placeholder.max(anon); @@ -1107,32 +1153,3 @@ impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { r.super_visit_with(self) } } - -/// Used to substitute specific `Regions`s with placeholders. -crate struct RegionsSubstitutor<'tcx> { - tcx: TyCtxt<'tcx>, - reempty_placeholder: ty::Region<'tcx>, -} - -impl<'tcx> RegionsSubstitutor<'tcx> { - crate fn new(tcx: TyCtxt<'tcx>, reempty_placeholder: ty::Region<'tcx>) -> Self { - RegionsSubstitutor { tcx, reempty_placeholder } - } -} - -impl<'tcx> TypeFolder<'tcx> for RegionsSubstitutor<'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - match r { - ty::ReEmpty(ui) => { - assert_eq!(ui.as_usize(), 0); - self.reempty_placeholder - } - - _ => r.super_fold_with(self), - } - } -} diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index a4d844e2eb..3c2a266dab 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -22,9 +22,8 @@ use rustc_infer::infer::canonical::{ use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal}; use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase; -use crate::chalk::lowering::{ - LowerInto, ParamsSubstitutor, PlaceholdersCollector, RegionsSubstitutor, -}; +use crate::chalk::lowering::LowerInto; +use crate::chalk::lowering::{ParamsSubstitutor, PlaceholdersCollector, ReverseParamsSubstitutor}; use chalk_solve::Solution; @@ -42,19 +41,10 @@ crate fn evaluate_goal<'tcx>( let mut placeholders_collector = PlaceholdersCollector::new(); obligation.visit_with(&mut placeholders_collector); - let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { - universe: ty::UniverseIndex::ROOT, - name: ty::BoundRegionKind::BrAnon(placeholders_collector.next_anon_region_placeholder + 1), - })); - let mut params_substitutor = ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder); let obligation = obligation.fold_with(&mut params_substitutor); - // FIXME(chalk): we really should be substituting these back in the solution - let _params: FxHashMap = params_substitutor.params; - - let mut regions_substitutor = RegionsSubstitutor::new(tcx, reempty_placeholder); - let obligation = obligation.fold_with(&mut regions_substitutor); + let params: FxHashMap = params_substitutor.params; let max_universe = obligation.max_universe.index(); @@ -85,7 +75,7 @@ crate fn evaluate_goal<'tcx>( chalk_ir::VariableKind::Lifetime, chalk_ir::UniverseIndex { counter: ui.index() }, ), - CanonicalVarKind::Const(_ui) => unimplemented!(), + CanonicalVarKind::Const(_ui, _ty) => unimplemented!(), CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(), }), ), @@ -96,7 +86,8 @@ crate fn evaluate_goal<'tcx>( use chalk_solve::Solver; let mut solver = chalk_engine::solve::SLGSolver::new(32, None); - let db = ChalkRustIrDatabase { interner, reempty_placeholder }; + let db = ChalkRustIrDatabase { interner }; + debug!(?lowered_goal); let solution = solver.solve(&db, &lowered_goal); debug!(?obligation, ?solution, "evaluate goal"); @@ -110,8 +101,9 @@ crate fn evaluate_goal<'tcx>( use rustc_middle::infer::canonical::CanonicalVarInfo; let mut var_values: IndexVec> = IndexVec::new(); + let mut reverse_param_substitutor = ReverseParamsSubstitutor::new(tcx, params); subst.as_slice(interner).iter().for_each(|p| { - var_values.push(p.lower_into(interner)); + var_values.push(p.lower_into(interner).fold_with(&mut reverse_param_substitutor)); }); let variables: Vec<_> = binders .iter(interner) @@ -127,9 +119,9 @@ crate fn evaluate_goal<'tcx>( 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), - ), + // FIXME(compiler-errors): We don't currently have a way of turning + // a Chalk ty back into a rustc ty, right? + chalk_ir::VariableKind::Const(_) => todo!(), }; CanonicalVarInfo { kind } }) diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 672e149b5f..455fc46a42 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -192,7 +192,7 @@ fn dtorck_constraint_for_ty<'tcx>( ty::Array(ety, _) | ty::Slice(ety) => { // single-element containers, behave like their element rustc_data_structures::stack::ensure_sufficient_stack(|| { - dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety, constraints) + dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, *ety, constraints) })?; } @@ -278,9 +278,9 @@ fn dtorck_constraint_for_ty<'tcx>( tcx.at(span).adt_dtorck_constraint(def.did)?; // FIXME: we can try to recursively `dtorck_constraint_on_ty` // there, but that needs some way to handle cycles. - constraints.dtorck_types.extend(dtorck_types.subst(tcx, substs)); - constraints.outlives.extend(outlives.subst(tcx, substs)); - constraints.overflows.extend(overflows.subst(tcx, substs)); + constraints.dtorck_types.extend(dtorck_types.iter().map(|t| t.subst(tcx, substs))); + constraints.outlives.extend(outlives.iter().map(|t| t.subst(tcx, substs))); + constraints.overflows.extend(overflows.iter().map(|t| t.subst(tcx, substs))); } // Objects must be alive in order for their destructor @@ -308,7 +308,7 @@ fn dtorck_constraint_for_ty<'tcx>( crate fn adt_dtorck_constraint( tcx: TyCtxt<'_>, def_id: DefId, -) -> Result, NoSolution> { +) -> Result<&DtorckConstraint<'_>, NoSolution> { let def = tcx.adt_def(def_id); let span = tcx.def_span(def_id); debug!("dtorck_constraint: {:?}", def); @@ -324,7 +324,7 @@ crate fn adt_dtorck_constraint( overflows: vec![], }; debug!("dtorck_constraint: {:?} => {:?}", def, result); - return Ok(result); + return Ok(tcx.arena.alloc(result)); } let mut result = DtorckConstraint::empty(); @@ -337,7 +337,7 @@ crate fn adt_dtorck_constraint( debug!("dtorck_constraint: {:?} => {:?}", def, result); - Ok(result) + Ok(tcx.arena.alloc(result)) } fn dedup_dtorck_constraint(c: &mut DtorckConstraint<'_>) { diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 46c2f7e4cf..a4aa965ec9 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -39,7 +39,7 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + // always only region relations, and we are about to // erase those anyway: debug_assert_eq!( - normalized_obligations.iter().find(|p| not_outlives_predicate(&p.predicate)), + normalized_obligations.iter().find(|p| not_outlives_predicate(p.predicate)), None, ); @@ -57,7 +57,7 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + }) } -fn not_outlives_predicate<'tcx>(p: &ty::Predicate<'tcx>) -> bool { +fn not_outlives_predicate<'tcx>(p: ty::Predicate<'tcx>) -> bool { match p.kind().skip_binder() { ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::TypeOutlives(..) => false, ty::PredicateKind::Trait(..) diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index a8e376838e..1de50bae31 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -36,7 +36,10 @@ fn normalize_projection_ty<'tcx>( &mut obligations, ); fulfill_cx.register_predicate_obligations(infcx, obligations); - Ok(NormalizationResult { normalized_ty: answer }) + // FIXME(associated_const_equality): All users of normalize_projection_ty expected + // a type, but there is the possibility it could've been a const now. Maybe change + // it to a Term later? + Ok(NormalizationResult { normalized_ty: answer.ty().unwrap() }) }, ) } diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs new file mode 100644 index 0000000000..4142c999ca --- /dev/null +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -0,0 +1,136 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::{self, TyCtxt}; + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { + associated_item, + associated_item_def_ids, + associated_items, + impl_item_implementor_ids, + trait_of_item, + ..*providers + }; +} + +fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { + let item = tcx.hir().expect_item(def_id.expect_local()); + match item.kind { + hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter( + trait_item_refs.iter().map(|trait_item_ref| trait_item_ref.id.def_id.to_def_id()), + ), + hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter( + impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.def_id.to_def_id()), + ), + hir::ItemKind::TraitAlias(..) => &[], + _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), + } +} + +fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> { + let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); + ty::AssocItems::new(items) +} + +fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> FxHashMap { + tcx.associated_items(impl_id) + .in_definition_order() + .filter_map(|item| item.trait_item_def_id.map(|trait_item| (trait_item, item.def_id))) + .collect() +} + +/// If the given `DefId` describes an item belonging to a trait, +/// returns the `DefId` of the trait that the trait item belongs to; +/// otherwise, returns `None`. +fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + tcx.opt_associated_item(def_id).and_then(|associated_item| match associated_item.container { + ty::TraitContainer(def_id) => Some(def_id), + ty::ImplContainer(_) => None, + }) +} + +fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { + let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let parent_def_id = tcx.hir().get_parent_item(id); + let parent_item = tcx.hir().expect_item(parent_def_id); + match parent_item.kind { + hir::ItemKind::Impl(ref impl_) => { + if let Some(impl_item_ref) = + impl_.items.iter().find(|i| i.id.def_id.to_def_id() == def_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); + return assoc_item; + } + } + + hir::ItemKind::Trait(.., ref trait_item_refs) => { + if let Some(trait_item_ref) = + trait_item_refs.iter().find(|i| i.id.def_id.to_def_id() == def_id) + { + let assoc_item = + associated_item_from_trait_item_ref(tcx, parent_def_id, trait_item_ref); + debug_assert_eq!(assoc_item.def_id, def_id); + return assoc_item; + } + } + + _ => {} + } + + span_bug!( + parent_item.span, + "unexpected parent of trait or impl item or item not found: {:?}", + parent_item.kind + ) +} + +fn associated_item_from_trait_item_ref( + tcx: TyCtxt<'_>, + parent_def_id: LocalDefId, + trait_item_ref: &hir::TraitItemRef, +) -> ty::AssocItem { + let def_id = trait_item_ref.id.def_id; + let (kind, has_self) = match trait_item_ref.kind { + hir::AssocItemKind::Const => (ty::AssocKind::Const, false), + hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), + hir::AssocItemKind::Type => (ty::AssocKind::Type, false), + }; + + ty::AssocItem { + name: trait_item_ref.ident.name, + kind, + vis: tcx.visibility(def_id), + defaultness: trait_item_ref.defaultness, + def_id: def_id.to_def_id(), + trait_item_def_id: Some(def_id.to_def_id()), + container: ty::TraitContainer(parent_def_id.to_def_id()), + fn_has_self_parameter: has_self, + } +} + +fn associated_item_from_impl_item_ref( + tcx: TyCtxt<'_>, + parent_def_id: LocalDefId, + impl_item_ref: &hir::ImplItemRef, +) -> ty::AssocItem { + let def_id = impl_item_ref.id.def_id; + let (kind, has_self) = match impl_item_ref.kind { + hir::AssocItemKind::Const => (ty::AssocKind::Const, false), + hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), + hir::AssocItemKind::Type => (ty::AssocKind::Type, false), + }; + + ty::AssocItem { + name: impl_item_ref.ident.name, + kind, + vis: tcx.visibility(def_id), + defaultness: impl_item_ref.defaultness, + def_id: def_id.to_def_id(), + trait_item_def_id: impl_item_ref.trait_item_def_id, + container: ty::ImplContainer(parent_def_id.to_def_id()), + fn_has_self_parameter: has_self, + } +} diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 13ffb2a5ad..91c4398c17 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -54,10 +54,6 @@ impl<'tcx> BoundVarsCollector<'tcx> { impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { type BreakTy = (); - fn tcx_for_anon_const_substs(&self) -> Option> { - // Anon const substs do not contain bound vars by default. - None - } fn visit_binder>( &mut self, t: &Binder<'tcx, T>, @@ -94,8 +90,8 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { } fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - match r { - ty::ReLateBound(index, br) if *index == self.binder_index => { + match *r { + ty::ReLateBound(index, br) if index == self.binder_index => { match self.vars.entry(br.var.as_u32()) { Entry::Vacant(entry) => { entry.insert(ty::BoundVariableKind::Region(br.kind)); @@ -152,8 +148,7 @@ fn inner_resolve_instance<'tcx>( let result = if let Some(trait_def_id) = tcx.trait_of_item(def.did) { debug!(" => associated item, attempting to find impl in param_env {:#?}", param_env); - let item = tcx.associated_item(def.did); - resolve_associated_item(tcx, &item, param_env, trait_def_id, substs) + resolve_associated_item(tcx, def.did, param_env, trait_def_id, substs) } else { let ty = tcx.type_of(def.def_id_for_type_of()); let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, ty); @@ -204,19 +199,12 @@ fn inner_resolve_instance<'tcx>( fn resolve_associated_item<'tcx>( tcx: TyCtxt<'tcx>, - trait_item: &ty::AssocItem, + trait_item_id: DefId, param_env: ty::ParamEnv<'tcx>, trait_id: DefId, rcvr_substs: SubstsRef<'tcx>, ) -> Result>, ErrorReported> { - let def_id = trait_item.def_id; - debug!( - "resolve_associated_item(trait_item={:?}, \ - param_env={:?}, \ - trait_id={:?}, \ - rcvr_substs={:?})", - def_id, param_env, trait_id, rcvr_substs - ); + debug!(?trait_item_id, ?param_env, ?trait_id, ?rcvr_substs, "resolve_associated_item"); let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); @@ -232,7 +220,7 @@ fn resolve_associated_item<'tcx>( traits::ImplSource::UserDefined(impl_data) => { debug!( "resolving ImplSource::UserDefined: {:?}, {:?}, {:?}, {:?}", - param_env, trait_item, rcvr_substs, impl_data + param_env, trait_item_id, rcvr_substs, impl_data ); assert!(!rcvr_substs.needs_infer()); assert!(!trait_ref.needs_infer()); @@ -241,9 +229,9 @@ fn resolve_associated_item<'tcx>( let trait_def = tcx.trait_def(trait_def_id); let leaf_def = trait_def .ancestors(tcx, impl_data.impl_def_id)? - .leaf_def(tcx, trait_item.ident, trait_item.kind) + .leaf_def(tcx, trait_item_id) .unwrap_or_else(|| { - bug!("{:?} not found in {:?}", trait_item, impl_data.impl_def_id); + bug!("{:?} not found in {:?}", trait_item_id, impl_data.impl_def_id); }); let substs = tcx.infer_ctxt().enter(|infcx| { @@ -297,22 +285,22 @@ fn resolve_associated_item<'tcx>( // performs (i.e. that the definition's type in the `impl` matches // the declaration in the `trait`), so that we can cheaply check // here if it failed, instead of approximating it. - if trait_item.kind == ty::AssocKind::Const - && trait_item.def_id != leaf_def.item.def_id + if leaf_def.item.kind == ty::AssocKind::Const + && trait_item_id != leaf_def.item.def_id && leaf_def.item.def_id.is_local() { let normalized_type_of = |def_id, substs| { tcx.subst_and_normalize_erasing_regions(substs, param_env, tcx.type_of(def_id)) }; - let original_ty = normalized_type_of(trait_item.def_id, rcvr_substs); + let original_ty = normalized_type_of(trait_item_id, rcvr_substs); let resolved_ty = normalized_type_of(leaf_def.item.def_id, substs); if original_ty != resolved_ty { let msg = format!( "Instance::resolve: inconsistent associated `const` type: \ was `{}: {}` but resolved to `{}: {}`", - tcx.def_path_str_with_substs(trait_item.def_id, rcvr_substs), + tcx.def_path_str_with_substs(trait_item_id, rcvr_substs), original_ty, tcx.def_path_str_with_substs(leaf_def.item.def_id, substs), resolved_ty, @@ -343,19 +331,22 @@ fn resolve_associated_item<'tcx>( } traits::ImplSource::FnPointer(ref data) => match data.fn_ty.kind() { ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { - def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + def: ty::InstanceDef::FnPtrShim(trait_item_id, data.fn_ty), substs: rcvr_substs, }), _ => None, }, traits::ImplSource::Object(ref data) => { - let index = traits::get_vtable_index_of_object_method(tcx, data, def_id); - Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs }) + let index = traits::get_vtable_index_of_object_method(tcx, data, trait_item_id); + Some(Instance { + def: ty::InstanceDef::Virtual(trait_item_id, index), + substs: rcvr_substs, + }) } traits::ImplSource::Builtin(..) => { if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() { // FIXME(eddyb) use lang items for methods instead of names. - let name = tcx.item_name(def_id); + let name = tcx.item_name(trait_item_id); if name == sym::clone { let self_ty = trait_ref.self_ty(); @@ -367,7 +358,7 @@ fn resolve_associated_item<'tcx>( }; Some(Instance { - def: ty::InstanceDef::CloneShim(def_id, self_ty), + def: ty::InstanceDef::CloneShim(trait_item_id, self_ty), substs: rcvr_substs, }) } else { @@ -375,7 +366,7 @@ fn resolve_associated_item<'tcx>( // Use the default `fn clone_from` from `trait Clone`. let substs = tcx.erase_regions(rcvr_substs); - Some(ty::Instance::new(def_id, substs)) + Some(ty::Instance::new(trait_item_id, substs)) } } else { None diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 60f8e196bc..55e1999076 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -16,6 +16,7 @@ extern crate tracing; use rustc_middle::ty::query::Providers; +mod assoc; mod common_traits; pub mod instance; mod needs_drop; @@ -23,6 +24,7 @@ pub mod representability; mod ty; pub fn provide(providers: &mut Providers) { + assoc::provide(providers); common_traits::provide(providers); needs_drop::provide(providers); ty::provide(providers); diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index d3eb9fd955..b08f8f6230 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -92,19 +92,14 @@ fn are_inner_types_recursive<'tcx>( seen, shadow_seen, representable_cache, - ty, + *ty, force_result, ), ty::Adt(def, substs) => { // Find non representable fields with their spans fold_repr(def.all_fields().map(|field| { let ty = field.ty(tcx, substs); - let span = match field - .did - .as_local() - .map(|id| tcx.hir().local_def_id_to_hir_id(id)) - .and_then(|id| tcx.hir().find(id)) - { + let span = match field.did.as_local().and_then(|id| tcx.hir().find_by_def_id(id)) { Some(hir::Node::Field(field)) => field.ty.span, _ => sp, }; @@ -260,7 +255,7 @@ fn is_type_structurally_recursive<'tcx>( force_result: &mut bool, ) -> Representability { debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp); - if let Some(representability) = representable_cache.get(ty) { + if let Some(representability) = representable_cache.get(&ty) { debug!( "is_type_structurally_recursive: {:?} {:?} - (cached) {:?}", ty, sp, representability @@ -327,7 +322,7 @@ fn is_type_structurally_recursive_inner<'tcx>( // struct Foo { Option> } for &seen_adt in iter { - if ty::TyS::same_type(ty, seen_adt) { + if ty == seen_adt { debug!("ContainsRecursive: {:?} contains {:?}", seen_adt, ty); return Representability::ContainsRecursive; } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 6c2657bd64..e44f80d5ac 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt}; use rustc_span::{sym, Span}; @@ -71,90 +71,6 @@ fn sized_constraint_for_ty<'tcx>( result } -fn associated_item_from_trait_item_ref( - tcx: TyCtxt<'_>, - parent_def_id: LocalDefId, - trait_item_ref: &hir::TraitItemRef, -) -> ty::AssocItem { - let def_id = trait_item_ref.id.def_id; - let (kind, has_self) = match trait_item_ref.kind { - hir::AssocItemKind::Const => (ty::AssocKind::Const, false), - hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), - hir::AssocItemKind::Type => (ty::AssocKind::Type, false), - }; - - ty::AssocItem { - ident: trait_item_ref.ident, - kind, - vis: tcx.visibility(def_id), - defaultness: trait_item_ref.defaultness, - def_id: def_id.to_def_id(), - container: ty::TraitContainer(parent_def_id.to_def_id()), - fn_has_self_parameter: has_self, - } -} - -fn associated_item_from_impl_item_ref( - tcx: TyCtxt<'_>, - parent_def_id: LocalDefId, - impl_item_ref: &hir::ImplItemRef, -) -> ty::AssocItem { - let def_id = impl_item_ref.id.def_id; - let (kind, has_self) = match impl_item_ref.kind { - hir::AssocItemKind::Const => (ty::AssocKind::Const, false), - hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self), - hir::AssocItemKind::Type => (ty::AssocKind::Type, false), - }; - - ty::AssocItem { - ident: impl_item_ref.ident, - kind, - vis: tcx.visibility(def_id), - defaultness: impl_item_ref.defaultness, - def_id: def_id.to_def_id(), - container: ty::ImplContainer(parent_def_id.to_def_id()), - fn_has_self_parameter: has_self, - } -} - -fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { - let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let parent_id = tcx.hir().get_parent_item(id); - let parent_def_id = tcx.hir().local_def_id(parent_id); - let parent_item = tcx.hir().expect_item(parent_def_id); - match parent_item.kind { - hir::ItemKind::Impl(ref impl_) => { - if let Some(impl_item_ref) = - impl_.items.iter().find(|i| i.id.def_id.to_def_id() == def_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); - return assoc_item; - } - } - - hir::ItemKind::Trait(.., ref trait_item_refs) => { - if let Some(trait_item_ref) = - trait_item_refs.iter().find(|i| i.id.def_id.to_def_id() == def_id) - { - let assoc_item = - associated_item_from_trait_item_ref(tcx, parent_def_id, trait_item_ref); - debug_assert_eq!(assoc_item.def_id, def_id); - return assoc_item; - } - } - - _ => {} - } - - span_bug!( - parent_item.span, - "unexpected parent of trait or impl item or item not found: {:?}", - parent_item.kind - ) -} - fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness { let item = tcx.hir().expect_item(def_id.expect_local()); if let hir::ItemKind::Impl(impl_) = &item.kind { @@ -197,25 +113,6 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstrain ty::AdtSizedConstraint(result) } -fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { - let item = tcx.hir().expect_item(def_id.expect_local()); - match item.kind { - hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter( - trait_item_refs.iter().map(|trait_item_ref| trait_item_ref.id.def_id.to_def_id()), - ), - hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter( - impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.def_id.to_def_id()), - ), - hir::ItemKind::TraitAlias(..) => &[], - _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), - } -} - -fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> { - let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)); - ty::AssocItems::new(items) -} - fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option { tcx.hir() .get_if_local(def_id) @@ -231,16 +128,6 @@ fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option { .map(|ident| ident.span) } -/// If the given `DefId` describes an item belonging to a trait, -/// returns the `DefId` of the trait that the trait item belongs to; -/// otherwise, returns `None`. -fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - tcx.opt_associated_item(def_id).and_then(|associated_item| match associated_item.container { - ty::TraitContainer(def_id) => Some(def_id), - ty::ImplContainer(_) => None, - }) -} - /// See `ParamEnv` struct definition for details. #[instrument(level = "debug", skip(tcx))] fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { @@ -262,7 +149,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // kind of an "idempotent" action, but I'm not sure where would be // a better place. In practice, we construct environments for // every fn once during type checking, and we'll abort if there - // are any errors at that point, so after type checking you can be + // are any errors at that point, so outside of type inference you can be // sure that this will succeed without errors anyway. if tcx.sess.opts.debugging_opts.chalk { @@ -270,16 +157,6 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { predicates.extend(environment); } - // It's important that we include the default substs in unevaluated - // constants, since `Unevaluated` instances in predicates whose substs are None - // can lead to "duplicate" caller bounds candidates during trait selection, - // duplicate in the sense that both have their default substs, but the - // candidate that resulted from a superpredicate still uses `None` in its - // `substs_` field of `Unevaluated` to indicate that it has its default substs, - // whereas the other candidate has `substs_: Some(default_substs)`, see - // issue #89334 - predicates = tcx.expose_default_const_substs(predicates); - let local_did = def_id.as_local(); let hir_id = local_did.map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)); @@ -392,8 +269,7 @@ fn well_formed_types_in_env<'tcx>( if !def_id.is_local() { return ty::List::empty(); } - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let node = tcx.hir().get(hir_id); + let node = tcx.hir().get_by_def_id(def_id.expect_local()); enum NodeKind { TraitImpl, @@ -447,7 +323,7 @@ fn well_formed_types_in_env<'tcx>( // constituents are well-formed. NodeKind::InherentImpl => { let self_ty = tcx.type_of(def_id); - inputs.extend(self_ty.walk(tcx)); + inputs.extend(self_ty.walk()); } // In an fn, we assume that the arguments and all their constituents are @@ -456,7 +332,7 @@ fn well_formed_types_in_env<'tcx>( let fn_sig = tcx.fn_sig(def_id); let fn_sig = tcx.liberate_late_bound_regions(def_id, fn_sig); - inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk(tcx))); + inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk())); } NodeKind::Other => (), @@ -534,7 +410,7 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { let self_ty = trait_ref.self_ty(); let self_ty_matches = match self_ty.kind() { - ty::Dynamic(ref data, ty::ReStatic) => data.principal().is_none(), + ty::Dynamic(ref data, re) if re.is_static() => data.principal().is_none(), _ => false, }; @@ -549,9 +425,7 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { /// Check if a function is async. fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - - let node = tcx.hir().get(hir_id); + let node = tcx.hir().get_by_def_id(def_id.expect_local()); let fn_kind = node.fn_kind().unwrap_or_else(|| { bug!("asyncness: expected fn-like node but got `{:?}`", def_id); @@ -600,7 +474,7 @@ pub fn conservative_is_privately_uninhabited_raw<'tcx>( Some(0) | None => false, // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. - Some(1..) => tcx.conservative_is_privately_uninhabited(param_env.and(ty)), + Some(1..) => tcx.conservative_is_privately_uninhabited(param_env.and(*ty)), } } ty::Ref(..) => { @@ -620,14 +494,10 @@ pub fn conservative_is_privately_uninhabited_raw<'tcx>( pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { asyncness, - associated_item, - associated_item_def_ids, - associated_items, adt_sized_constraint, def_ident_span, param_env, param_env_reveal_all_normalized, - trait_of_item, instance_def_size_estimate, issue33140_self_ty, impl_defaultness, diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index f11c93e933..e26f003315 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -19,116 +19,94 @@ bitflags! { // Does this have parameters? Used to determine whether substitution is // required. /// Does this have `Param`? - const HAS_KNOWN_TY_PARAM = 1 << 0; + const HAS_TY_PARAM = 1 << 0; /// Does this have `ReEarlyBound`? - const HAS_KNOWN_RE_PARAM = 1 << 1; + const HAS_RE_PARAM = 1 << 1; /// Does this have `ConstKind::Param`? - const HAS_KNOWN_CT_PARAM = 1 << 2; + const HAS_CT_PARAM = 1 << 2; - const KNOWN_NEEDS_SUBST = TypeFlags::HAS_KNOWN_TY_PARAM.bits - | TypeFlags::HAS_KNOWN_RE_PARAM.bits - | TypeFlags::HAS_KNOWN_CT_PARAM.bits; + const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_RE_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits; /// Does this have `Infer`? - const HAS_TY_INFER = 1 << 3; + const HAS_TY_INFER = 1 << 3; /// Does this have `ReVar`? - const HAS_RE_INFER = 1 << 4; + const HAS_RE_INFER = 1 << 4; /// Does this have `ConstKind::Infer`? - const HAS_CT_INFER = 1 << 5; + const HAS_CT_INFER = 1 << 5; /// Does this have inference variables? Used to determine whether /// inference is required. - const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_RE_INFER.bits - | TypeFlags::HAS_CT_INFER.bits; + const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_RE_INFER.bits + | TypeFlags::HAS_CT_INFER.bits; /// Does this have `Placeholder`? - const HAS_TY_PLACEHOLDER = 1 << 6; + const HAS_TY_PLACEHOLDER = 1 << 6; /// Does this have `RePlaceholder`? - const HAS_RE_PLACEHOLDER = 1 << 7; + const HAS_RE_PLACEHOLDER = 1 << 7; /// Does this have `ConstKind::Placeholder`? - const HAS_CT_PLACEHOLDER = 1 << 8; + const HAS_CT_PLACEHOLDER = 1 << 8; /// `true` if there are "names" of regions and so forth /// that are local to a particular fn/inferctxt - const HAS_KNOWN_FREE_LOCAL_REGIONS = 1 << 9; + const HAS_FREE_LOCAL_REGIONS = 1 << 9; /// `true` if there are "names" of types and regions and so forth /// that are local to a particular fn - const HAS_KNOWN_FREE_LOCAL_NAMES = TypeFlags::HAS_KNOWN_TY_PARAM.bits - | TypeFlags::HAS_KNOWN_CT_PARAM.bits - | TypeFlags::HAS_TY_INFER.bits - | TypeFlags::HAS_CT_INFER.bits - | TypeFlags::HAS_TY_PLACEHOLDER.bits - | TypeFlags::HAS_CT_PLACEHOLDER.bits - // We consider 'freshened' types and constants - // to depend on a particular fn. - // The freshening process throws away information, - // which can make things unsuitable for use in a global - // cache. Note that there is no 'fresh lifetime' flag - - // freshening replaces all lifetimes with `ReErased`, - // which is different from how types/const are freshened. - | TypeFlags::HAS_TY_FRESH.bits - | TypeFlags::HAS_CT_FRESH.bits - | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS.bits; - - const HAS_POTENTIAL_FREE_LOCAL_NAMES = TypeFlags::HAS_KNOWN_FREE_LOCAL_NAMES.bits - | TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS.bits; + const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits + | TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_CT_INFER.bits + | TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits + // We consider 'freshened' types and constants + // to depend on a particular fn. + // The freshening process throws away information, + // which can make things unsuitable for use in a global + // cache. Note that there is no 'fresh lifetime' flag - + // freshening replaces all lifetimes with `ReErased`, + // which is different from how types/const are freshened. + | TypeFlags::HAS_TY_FRESH.bits + | TypeFlags::HAS_CT_FRESH.bits + | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; /// Does this have `Projection`? - const HAS_TY_PROJECTION = 1 << 10; + const HAS_TY_PROJECTION = 1 << 10; /// Does this have `Opaque`? - const HAS_TY_OPAQUE = 1 << 11; + const HAS_TY_OPAQUE = 1 << 11; /// Does this have `ConstKind::Unevaluated`? - const HAS_CT_PROJECTION = 1 << 12; + const HAS_CT_PROJECTION = 1 << 12; /// Could this type be normalized further? - const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits - | TypeFlags::HAS_TY_OPAQUE.bits - | TypeFlags::HAS_CT_PROJECTION.bits; + const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits + | TypeFlags::HAS_TY_OPAQUE.bits + | TypeFlags::HAS_CT_PROJECTION.bits; /// Is an error type/const reachable? - const HAS_ERROR = 1 << 13; + const HAS_ERROR = 1 << 13; /// Does this have any region that "appears free" in the type? /// Basically anything but `ReLateBound` and `ReErased`. - const HAS_KNOWN_FREE_REGIONS = 1 << 14; - - const HAS_POTENTIAL_FREE_REGIONS = TypeFlags::HAS_KNOWN_FREE_REGIONS.bits - | TypeFlags::HAS_UNKNOWN_DEFAULT_CONST_SUBSTS.bits; + const HAS_FREE_REGIONS = 1 << 14; /// Does this have any `ReLateBound` regions? Used to check /// if a global bound is safe to evaluate. - const HAS_RE_LATE_BOUND = 1 << 15; + const HAS_RE_LATE_BOUND = 1 << 15; /// Does this have any `ReErased` regions? - const HAS_RE_ERASED = 1 << 16; + const HAS_RE_ERASED = 1 << 16; /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? - /// - /// Note that this flag being set is not a guarantee, as it is also - /// set if there are any anon consts with unknown default substs. - const STILL_FURTHER_SPECIALIZABLE = 1 << 17; + const STILL_FURTHER_SPECIALIZABLE = 1 << 17; /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 18; + const HAS_TY_FRESH = 1 << 18; /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 19; - - /// Does this value have unknown default anon const substs. - /// - /// For more details refer to... - /// FIXME(@lcnr): ask me for now, still have to write all of this. - const HAS_UNKNOWN_DEFAULT_CONST_SUBSTS = 1 << 20; - /// Flags which can be influenced by default anon const substs. - const MAY_NEED_DEFAULT_CONST_SUBSTS = TypeFlags::HAS_KNOWN_RE_PARAM.bits - | TypeFlags::HAS_KNOWN_TY_PARAM.bits - | TypeFlags::HAS_KNOWN_CT_PARAM.bits - | TypeFlags::HAS_KNOWN_FREE_LOCAL_REGIONS.bits - | TypeFlags::HAS_KNOWN_FREE_REGIONS.bits; - + const HAS_CT_FRESH = 1 << 19; } } @@ -422,9 +400,11 @@ pub enum InferTy { /// they carry no values. impl UnifyKey for TyVid { type Value = (); + #[inline] fn index(&self) -> u32 { self.as_u32() } + #[inline] fn from_index(i: u32) -> TyVid { TyVid::from_u32(i) } @@ -441,6 +421,7 @@ impl UnifyKey for IntVid { fn index(&self) -> u32 { self.index } + #[inline] fn from_index(i: u32) -> IntVid { IntVid { index: i } } @@ -453,9 +434,11 @@ impl EqUnifyValue for FloatVarValue {} impl UnifyKey for FloatVid { type Value = Option; + #[inline] fn index(&self) -> u32 { self.index } + #[inline] fn from_index(i: u32) -> FloatVid { FloatVid { index: i } } diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index 7e570e151c..57930a28a3 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -15,6 +15,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } +rustc_graphviz = { path = "../rustc_graphviz" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_target = { path = "../rustc_target" } @@ -27,3 +28,4 @@ rustc_infer = { path = "../rustc_infer" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } rustc_lint = { path = "../rustc_lint" } +rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index ea54b85b2f..7e9abe3a25 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -93,62 +93,100 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, trait_def_id: DefId, trait_segment: &'_ hir::PathSegment<'_>, + is_impl: bool, ) { + if self.tcx().features().unboxed_closures { + return; + } + let trait_def = self.tcx().trait_def(trait_def_id); + if !trait_def.paren_sugar { + if trait_segment.args().parenthesized { + // For now, require that parenthetical notation be used only with `Fn()` etc. + let mut err = feature_err( + &self.tcx().sess.parse_sess, + sym::unboxed_closures, + span, + "parenthetical notation is only stable when used with `Fn`-family traits", + ); + err.emit(); + } - if !self.tcx().features().unboxed_closures - && trait_segment.args().parenthesized != trait_def.paren_sugar - { - let sess = &self.tcx().sess.parse_sess; + return; + } + + let sess = self.tcx().sess; + + if !trait_segment.args().parenthesized { // For now, require that parenthetical notation be used only with `Fn()` etc. - let (msg, sugg) = if trait_def.paren_sugar { - ( - "the precise format of `Fn`-family traits' type parameters is subject to \ - change", - Some(format!( - "{}{} -> {}", - trait_segment.ident, - trait_segment - .args - .as_ref() - .and_then(|args| args.args.get(0)) - .and_then(|arg| match arg { - hir::GenericArg::Type(ty) => match ty.kind { - hir::TyKind::Tup(t) => t - .iter() - .map(|e| sess.source_map().span_to_snippet(e.span)) - .collect::, _>>() - .map(|a| a.join(", ")), - _ => sess.source_map().span_to_snippet(ty.span), - } - .map(|s| format!("({})", s)) - .ok(), - _ => None, - }) - .unwrap_or_else(|| "()".to_string()), - trait_segment - .args() - .bindings - .iter() - .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { - (true, hir::TypeBindingKind::Equality { ty }) => { - sess.source_map().span_to_snippet(ty.span).ok() - } - _ => None, - }) - .unwrap_or_else(|| "()".to_string()), - )), - ) - } else { - ("parenthetical notation is only stable when used with `Fn`-family traits", None) - }; - let mut err = feature_err(sess, sym::unboxed_closures, span, msg); - if let Some(sugg) = sugg { - let msg = "use parenthetical notation instead"; - err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); + let mut err = feature_err( + &sess.parse_sess, + sym::unboxed_closures, + span, + "the precise format of `Fn`-family traits' type parameters is subject to change", + ); + // Do not suggest the other syntax if we are in trait impl: + // the desugaring would contain an associated type constrait. + if !is_impl { + let args = trait_segment + .args + .as_ref() + .and_then(|args| args.args.get(0)) + .and_then(|arg| match arg { + hir::GenericArg::Type(ty) => match ty.kind { + hir::TyKind::Tup(t) => t + .iter() + .map(|e| sess.source_map().span_to_snippet(e.span)) + .collect::, _>>() + .map(|a| a.join(", ")), + _ => sess.source_map().span_to_snippet(ty.span), + } + .map(|s| format!("({})", s)) + .ok(), + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + let ret = trait_segment + .args() + .bindings + .iter() + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { + (true, hir::TypeBindingKind::Equality { term }) => { + let span = match term { + hir::Term::Ty(ty) => ty.span, + hir::Term::Const(c) => self.tcx().hir().span(c.hir_id), + }; + sess.source_map().span_to_snippet(span).ok() + } + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + err.span_suggestion( + span, + "use parenthetical notation instead", + format!("{}{} -> {}", trait_segment.ident, args, ret), + Applicability::MaybeIncorrect, + ); } err.emit(); } + + if is_impl { + let trait_name = self.tcx().def_path_str(trait_def_id); + struct_span_err!( + self.tcx().sess, + span, + E0183, + "manual implementations of `{}` are experimental", + trait_name, + ) + .span_label( + span, + format!("manual implementations of `{}` are experimental", trait_name), + ) + .help("add `#![feature(unboxed_closures)]` to the crate attributes to enable") + .emit(); + } } pub(crate) fn complain_about_assoc_type_not_found( @@ -173,10 +211,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); let all_candidate_names: Vec<_> = all_candidates() - .map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) - .flatten() + .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) .filter_map( - |item| if item.kind == ty::AssocKind::Type { Some(item.ident.name) } else { None }, + |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None }, ) .collect(); @@ -232,7 +269,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let trait_def_id = assoc_item.container.id(); names.push(format!( "`{}` (from trait `{}`)", - assoc_item.ident, + assoc_item.name, tcx.def_path_str(trait_def_id), )); } @@ -289,11 +326,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut names: FxHashMap<_, usize> = FxHashMap::default(); for item in assoc_items { types_count += 1; - *names.entry(item.ident.name).or_insert(0) += 1; + *names.entry(item.name).or_insert(0) += 1; } let mut dupes = false; for item in assoc_items { - let prefix = if names[&item.ident.name] > 1 { + let prefix = if names[&item.name] > 1 { let trait_def_id = item.container.id(); dupes = true; format!("{}::", tcx.def_path_str(trait_def_id)) @@ -301,7 +338,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { String::new() }; if let Some(sp) = tcx.hir().span_if_local(item.def_id) { - err.span_label(sp, format!("`{}{}` defined here", prefix, item.ident)); + err.span_label(sp, format!("`{}{}` defined here", prefix, item.name)); } } if potential_assoc_types.len() == assoc_items.len() { @@ -312,14 +349,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // `Iterator`. for (potential, item) in iter::zip(&potential_assoc_types, assoc_items) { if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) { - suggestions.push((*potential, format!("{} = {}", item.ident, snippet))); + suggestions.push((*potential, format!("{} = {}", item.name, snippet))); } } } else if let (Ok(snippet), false) = (tcx.sess.source_map().span_to_snippet(*span), dupes) { let types: Vec<_> = - assoc_items.iter().map(|item| format!("{} = Type", item.ident)).collect(); + assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect(); let code = if snippet.ends_with('>') { // The user wrote `Trait<'a>` or similar and we don't have a type we can // suggest, but at least we can clue them to the correct syntax @@ -350,17 +387,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut names: FxHashMap<_, usize> = FxHashMap::default(); for item in assoc_items { types_count += 1; - *names.entry(item.ident.name).or_insert(0) += 1; + *names.entry(item.name).or_insert(0) += 1; } let mut label = vec![]; for item in assoc_items { - let postfix = if names[&item.ident.name] > 1 { + let postfix = if names[&item.name] > 1 { let trait_def_id = item.container.id(); format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) } else { String::new() }; - label.push(format!("`{}`{}", item.ident, postfix)); + label.push(format!("`{}`{}", item.name, postfix)); } if !label.is_empty() { err.span_label( diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 956696546d..05ff7f818c 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -445,7 +445,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; let infer_lifetimes = - gen_pos != GenericArgPosition::Type && !gen_args.has_lifetime_params(); + (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); if gen_pos != GenericArgPosition::Type && !gen_args.bindings.is_empty() { Self::prohibit_assoc_ty_binding(tcx, gen_args.bindings[0].span); diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 8226ffbccc..96f9204998 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -6,7 +6,7 @@ mod errors; mod generics; use crate::bounds::Bounds; -use crate::collect::PlaceholderHirTyCollector; +use crate::collect::HirPlaceholderCollector; use crate::errors::{ AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits, TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified, @@ -83,7 +83,7 @@ pub trait AstConv<'tcx> { ty: Ty<'tcx>, param: Option<&ty::GenericParamDef>, span: Span, - ) -> &'tcx Const<'tcx>; + ) -> Const<'tcx>; /// Projecting an associated type from a (potentially) /// higher-ranked trait reference is more complicated, because of @@ -123,7 +123,7 @@ struct ConvertedBinding<'a, 'tcx> { #[derive(Debug)] enum ConvertedBindingKind<'a, 'tcx> { - Equality(Ty<'tcx>), + Equality(ty::Term<'tcx>), Constraint(&'a [hir::GenericBound<'a>]), } @@ -288,7 +288,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Given the type/lifetime/const arguments provided to some path (along with /// an implicit `Self`, if this is a trait reference), returns the complete /// set of substitutions. This may involve applying defaulted type parameters. - /// Also returns back constraints on associated types. + /// Constraints on associated typess are created from `create_assoc_bindings_for_generic_args`. /// /// Example: /// @@ -302,7 +302,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// which will have been resolved to a `def_id` /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type /// parameters are returned in the `SubstsRef`, the associated type bindings like - /// `Output = u32` are returned in the `Vec` result. + /// `Output = u32` are returned from `create_assoc_bindings_for_generic_args`. /// /// Note that the type listing given here is *exactly* what the user provided. /// @@ -388,7 +388,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if self.is_object && has_default { let default_ty = tcx.at(self.span).type_of(param.def_id); let self_param = tcx.types.self_param; - if default_ty.walk(tcx).any(|arg| arg == self_param.into()) { + if default_ty.walk().any(|arg| arg == self_param.into()) { // There is no suitable inference default for a type parameter // that references self, in an object type. return true; @@ -482,7 +482,20 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) -> subst::GenericArg<'tcx> { let tcx = self.astconv.tcx(); match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(), + GenericParamDefKind::Lifetime => self + .astconv + .re_infer(Some(param), self.span) + .unwrap_or_else(|| { + debug!(?param, "unelided lifetime in signature"); + + // This indicates an illegal lifetime in a non-assoc-trait position + tcx.sess.delay_span_bug(self.span, "unelided lifetime in signature"); + + // Supply some dummy value. We don't have an + // `re_error`, annoyingly, so use `'static`. + tcx.lifetimes.re_static + }) + .into(), GenericParamDefKind::Type { has_default, .. } => { if !infer_args && has_default { // No type parameter provided, but a default exists. @@ -601,10 +614,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .iter() .map(|binding| { let kind = match binding.kind { - hir::TypeBindingKind::Equality { ty } => { - ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty)) - } - hir::TypeBindingKind::Constraint { bounds } => { + hir::TypeBindingKind::Equality { ref term } => match term { + hir::Term::Ty(ref ty) => { + ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into()) + } + hir::Term::Const(ref c) => { + let local_did = self.tcx().hir().local_def_id(c.hir_id); + let c = Const::from_anon_const(self.tcx(), local_did); + ConvertedBindingKind::Equality(c.into()) + } + }, + hir::TypeBindingKind::Constraint { ref bounds } => { ConvertedBindingKind::Constraint(bounds) } }; @@ -669,6 +689,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), self_ty, trait_ref.path.segments.last().unwrap(), + true, ) } @@ -765,7 +786,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let infer_args = trait_segment.infer_args; self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); - self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment); + self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); self.instantiate_poly_trait_ref_inner( hir_id, @@ -822,9 +843,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_def_id: DefId, self_ty: Ty<'tcx>, trait_segment: &hir::PathSegment<'_>, + is_impl: bool, ) -> ty::TraitRef<'tcx> { - let (substs, _) = - self.create_substs_for_ast_trait_ref(span, trait_def_id, self_ty, trait_segment); + let (substs, _) = self.create_substs_for_ast_trait_ref( + span, + trait_def_id, + self_ty, + trait_segment, + is_impl, + ); let assoc_bindings = self.create_assoc_bindings_for_generic_args(trait_segment.args()); if let Some(b) = assoc_bindings.first() { Self::prohibit_assoc_ty_binding(self.tcx(), b.span); @@ -839,8 +866,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_def_id: DefId, self_ty: Ty<'tcx>, trait_segment: &'a hir::PathSegment<'a>, + is_impl: bool, ) -> (SubstsRef<'tcx>, GenericArgCountResult) { - self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment); + self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); self.create_substs_for_ast_path( span, @@ -859,6 +887,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id) .is_some() } + fn trait_defines_associated_const_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool { + self.tcx() + .associated_items(trait_def_id) + .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Const, trait_def_id) + .is_some() + } // Sets `implicitly_sized` to true on `Bounds` if necessary pub(crate) fn add_implicitly_sized<'hir>( @@ -1106,34 +1140,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. - let assoc_ty = tcx - .associated_items(candidate.def_id()) - .filter_by_name_unhygienic(assoc_ident.name) - .find(|i| { - i.kind == ty::AssocKind::Type && i.ident.normalize_to_macros_2_0() == assoc_ident - }) + let find_item_of_kind = |kind| { + tcx.associated_items(candidate.def_id()) + .filter_by_name_unhygienic(assoc_ident.name) + .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) + }; + let assoc_item = find_item_of_kind(ty::AssocKind::Type) + .or_else(|| find_item_of_kind(ty::AssocKind::Const)) .expect("missing associated type"); - if !assoc_ty.vis.is_accessible_from(def_scope, tcx) { + if !assoc_item.vis.is_accessible_from(def_scope, tcx) { + let kind = match assoc_item.kind { + ty::AssocKind::Type => "type", + ty::AssocKind::Const => "const", + _ => unreachable!(), + }; tcx.sess .struct_span_err( binding.span, - &format!("associated type `{}` is private", binding.item_name), + &format!("associated {kind} `{}` is private", binding.item_name), ) - .span_label(binding.span, "private associated type") + .span_label(binding.span, &format!("private associated {kind}")) .emit(); } - tcx.check_stability(assoc_ty.def_id, Some(hir_ref_id), binding.span, None); + tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); if !speculative { dup_bindings - .entry(assoc_ty.def_id) + .entry(assoc_item.def_id) .and_modify(|prev_span| { self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified { span: binding.span, prev_span: *prev_span, item_name: binding.item_name, - def_path: tcx.def_path_str(assoc_ty.container.id()), + def_path: tcx.def_path_str(assoc_item.container.id()), }); }) .or_insert(binding.span); @@ -1141,7 +1181,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Include substitutions for generic parameters of associated types let projection_ty = candidate.map_bound(|trait_ref| { - let ident = Ident::new(assoc_ty.ident.name, binding.item_name.span); + let ident = Ident::new(assoc_item.name, binding.item_name.span); let item_segment = hir::PathSegment { ident, hir_id: Some(binding.hir_id), @@ -1153,7 +1193,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item( tcx, path_span, - assoc_ty.def_id, + assoc_item.def_id, &item_segment, trait_ref.substs, ); @@ -1164,14 +1204,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); ty::ProjectionTy { - item_def_id: assoc_ty.def_id, + item_def_id: assoc_item.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. + // declared in the trait-ref or assoc_item. These are not well-formed. // // Example: // @@ -1207,18 +1247,35 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } match binding.kind { - ConvertedBindingKind::Equality(ty) => { + ConvertedBindingKind::Equality(term) => { // "Desugar" a constraint like `T: Iterator` this to // the "projection predicate" for: // // `::Item = u32` + let assoc_item_def_id = projection_ty.skip_binder().item_def_id; + let def_kind = tcx.def_kind(assoc_item_def_id); + match (def_kind, term) { + (hir::def::DefKind::AssocTy, ty::Term::Ty(_)) + | (hir::def::DefKind::AssocConst, ty::Term::Const(_)) => (), + (_, _) => { + let got = if let ty::Term::Ty(_) = term { "type" } else { "const" }; + let expected = def_kind.descr(assoc_item_def_id); + tcx.sess + .struct_span_err( + binding.span, + &format!("mismatch in bind of {expected}, got {got}"), + ) + .span_note( + tcx.def_span(assoc_item_def_id), + &format!("{expected} defined here does not match {got}"), + ) + .emit(); + } + } bounds.projection_bounds.push(( - 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 } + projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate { + projection_ty, + term: term, }), binding.span, )); @@ -1369,8 +1426,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { 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. - let references_self = - pred.skip_binder().ty.walk(tcx).any(|arg| arg == dummy_self.into()); + let references_self = match pred.skip_binder().term { + ty::Term::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()), + ty::Term::Const(c) => c.ty().walk().any(|arg| arg == dummy_self.into()), + }; // If the projection output contains `Self`, force the user to // elaborate it explicitly to avoid a lot of complexity. @@ -1594,10 +1653,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { { let mut matching_candidates = all_candidates() .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name)); + let mut const_candidates = all_candidates() + .filter(|r| self.trait_defines_associated_const_named(r.def_id(), assoc_name)); - let bound = match matching_candidates.next() { - Some(bound) => bound, - None => { + let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) { + (Some(bound), _) => (bound, matching_candidates.next()), + (None, Some(bound)) => (bound, const_candidates.next()), + (None, None) => { self.complain_about_assoc_type_not_found( all_candidates, &ty_param_name(), @@ -1607,10 +1669,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Err(ErrorReported); } }; - debug!("one_bound_for_assoc_type: bound = {:?}", bound); - if let Some(bound2) = matching_candidates.next() { + if let Some(bound2) = next_cand { debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2); let is_equality = is_equality(); @@ -1695,6 +1756,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Err(ErrorReported); } } + Ok(bound) } @@ -1727,7 +1789,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let variant_def = adt_def .variants .iter() - .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident, adt_def.did)); + .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did)); if let Some(variant_def) = variant_def { if permit_variants { tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); @@ -1743,7 +1805,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Find the type of the associated item, and the trait where the associated // item is declared. let bound = match (&qself_ty.kind(), qself_res) { - (_, Res::SelfTy(Some(_), Some((impl_def_id, _)))) => { + (_, Res::SelfTy { trait_: Some(_), alias_to: Some((impl_def_id, _)) }) => { // `Self` in an impl of a trait -- we have a concrete self type and a // trait reference. let trait_ref = match tcx.impl_trait_ref(impl_def_id) { @@ -1764,7 +1826,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } ( &ty::Param(_), - Res::SelfTy(Some(param_did), None) | Res::Def(DefKind::TyParam, param_did), + Res::SelfTy { trait_: Some(param_did), alias_to: None } + | Res::Def(DefKind::TyParam, param_did), ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?, _ => { if variant_resolution.is_some() { @@ -1786,7 +1849,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &adt_def .variants .iter() - .map(|variant| variant.ident.name) + .map(|variant| variant.name) .collect::>(), assoc_ident.name, None, @@ -1829,14 +1892,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. - let item = tcx - .associated_items(trait_did) - .in_definition_order() - .find(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident.normalize_to_macros_2_0() == assoc_ident - }) - .expect("missing associated type"); + let item = tcx.associated_items(trait_did).in_definition_order().find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident + }); + // Assume that if it's not matched, there must be a const defined with the same name + // but it was used in a type position. + let Some(item) = item else { + let msg = format!("found associated const `{assoc_ident}` when type was expected"); + tcx.sess.struct_span_err(span, &msg).emit(); + return Err(ErrorReported); + }; let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound); let ty = self.normalize_ty(span, ty); @@ -1906,7 +1972,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .and_then(|def_id| { def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) }) - .map(|hir_id| tcx.hir().get_parent_did(hir_id).to_def_id()); + .map(|hir_id| tcx.hir().get_parent_item(hir_id).to_def_id()); debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id); @@ -1932,7 +1998,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("qpath_to_ty: self_type={:?}", self_ty); - let trait_ref = self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment); + let trait_ref = + self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false); let item_substs = self.create_substs_for_associated_item( tcx, @@ -2204,19 +2271,38 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let index = generics.param_def_id_to_index[&def_id]; tcx.mk_ty_param(index, tcx.hir().name(hir_id)) } - Res::SelfTy(Some(_), None) => { + Res::SelfTy { trait_: Some(_), alias_to: None } => { // `Self` in trait or type alias. assert_eq!(opt_self_ty, None); self.prohibit_generics(path.segments); tcx.types.self_param } - Res::SelfTy(_, Some((def_id, forbid_generic))) => { + Res::SelfTy { trait_: _, alias_to: Some((def_id, forbid_generic)) } => { // `Self` in impl (we know the concrete type). assert_eq!(opt_self_ty, None); self.prohibit_generics(path.segments); // Try to evaluate any array length constants. - let normalized_ty = self.normalize_ty(span, tcx.at(span).type_of(def_id)); - if forbid_generic && normalized_ty.definitely_needs_subst(tcx) { + let ty = tcx.at(span).type_of(def_id); + // HACK(min_const_generics): Forbid generic `Self` types + // here as we can't easily do that during nameres. + // + // We do this before normalization as we otherwise allow + // ```rust + // trait AlwaysApplicable { type Assoc; } + // impl AlwaysApplicable for T { type Assoc = usize; } + // + // trait BindsParam { + // type ArrayTy; + // } + // impl BindsParam for ::Assoc { + // type ArrayTy = [u8; Self::MAX]; + // } + // ``` + // Note that the normalization happens in the param env of + // the anon const, which is empty. This is why the + // `AlwaysApplicable` impl needs a `T: ?Sized` bound for + // this to compile if we were to normalize here. + if forbid_generic && ty.needs_subst() { let mut err = tcx.sess.struct_span_err( path.span, "generic `Self` types are currently not permitted in anonymous constants", @@ -2231,7 +2317,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.emit(); tcx.ty_error() } else { - normalized_ty + self.normalize_ty(span, ty) } } Res::Def(DefKind::AssocTy, def_id) => { @@ -2469,7 +2555,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!(?bound_vars); // We proactively collect all the inferred type params to emit a single error per fn def. - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); for ty in decl.inputs { visitor.visit_ty(ty); } @@ -2588,7 +2674,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // If any of the derived region bounds are 'static, that is always // the best choice. - if derived_region_bounds.iter().any(|&r| ty::ReStatic == *r) { + if derived_region_bounds.iter().any(|r| r.is_static()) { return Some(tcx.lifetimes.re_static); } diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_typeck/src/bounds.rs index 8bc3a48e5b..6a28bb16a2 100644 --- a/compiler/rustc_typeck/src/bounds.rs +++ b/compiler/rustc_typeck/src/bounds.rs @@ -48,14 +48,19 @@ impl<'tcx> Bounds<'tcx> { /// where-clauses). Because some of our bounds listings (e.g., /// regions) don't include the self-type, you must supply the /// self-type here (the `param_ty` parameter). - pub fn predicates( - &self, + pub fn predicates<'out, 's>( + &'s self, tcx: TyCtxt<'tcx>, param_ty: Ty<'tcx>, - ) -> Vec<(ty::Predicate<'tcx>, Span)> { + // the output must live shorter than the duration of the borrow of self and 'tcx. + ) -> impl Iterator, Span)> + 'out + where + 'tcx: 'out, + 's: 'out, + { // If it could be sized, and is, add the `Sized` predicate. let sized_predicate = self.implicitly_sized.and_then(|span| { - tcx.lang_items().sized_trait().map(|sized| { + tcx.lang_items().sized_trait().map(move |sized| { let trait_ref = ty::Binder::dummy(ty::TraitRef { def_id: sized, substs: tcx.mk_substs_trait(param_ty, &[]), @@ -64,25 +69,22 @@ impl<'tcx> Bounds<'tcx> { }) }); - sized_predicate - .into_iter() - .chain(self.region_bounds.iter().map(|&(region_bound, span)| { - ( - region_bound - .map_bound(|region_bound| ty::OutlivesPredicate(param_ty, region_bound)) - .to_predicate(tcx), - span, - ) - })) - .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { + let region_preds = self.region_bounds.iter().map(move |&(region_bound, span)| { + let pred = region_bound + .map_bound(|region_bound| ty::OutlivesPredicate(param_ty, region_bound)) + .to_predicate(tcx); + (pred, span) + }); + let trait_bounds = + self.trait_bounds.iter().map(move |&(bound_trait_ref, span, constness)| { let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); (predicate, span) - })) - .chain( - self.projection_bounds - .iter() - .map(|&(projection, span)| (projection.to_predicate(tcx), span)), - ) - .collect() + }); + let projection_bounds = self + .projection_bounds + .iter() + .map(move |&(projection, span)| (projection.to_predicate(tcx), span)); + + sized_predicate.into_iter().chain(region_preds).chain(trait_bounds).chain(projection_bounds) } } diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 405e4e8594..3701b255b7 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -4,7 +4,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; 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, TyS}; +use rustc_middle::ty::{self, ToPredicate, Ty}; use rustc_span::{MultiSpan, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -505,7 +505,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn opt_suggest_box_span( &self, span: Span, - outer_ty: &'tcx TyS<'tcx>, + outer_ty: Ty<'tcx>, orig_expected: Expectation<'tcx>, ) -> Option { match (orig_expected, self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty))) { diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index eea8f40635..f64a90ed10 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -269,7 +269,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, }; autoref = Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[0], }); } @@ -307,6 +307,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Give appropriate suggestion when encountering `[("a", 0) ("b", 1)]`, where the + /// likely intention is to create an array containing tuples. + fn maybe_suggest_bad_array_definition( + &self, + err: &mut DiagnosticBuilder<'a>, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + ) -> bool { + let hir_id = self.tcx.hir().get_parent_node(call_expr.hir_id); + let parent_node = self.tcx.hir().get(hir_id); + if let ( + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }), + hir::ExprKind::Tup(exp), + hir::ExprKind::Call(_, args), + ) = (parent_node, &callee_expr.kind, &call_expr.kind) + { + if args.len() == exp.len() { + let start = callee_expr.span.shrink_to_hi(); + err.span_suggestion( + start, + "consider separating array elements with a comma", + ",".to_string(), + Applicability::MaybeIncorrect, + ); + return true; + } + } + false + } + fn confirm_builtin_call( &self, call_expr: &'tcx hir::Expr<'tcx>, @@ -422,7 +452,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => Res::Err, }; - err.span_label(call_expr.span, "call expression requires function"); + if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { + err.span_label(call_expr.span, "call expression requires function"); + } if let Some(span) = self.tcx.hir().res_span(def) { let callee_ty = callee_ty.to_string(); @@ -596,7 +628,7 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { for (method_arg_ty, self_arg_ty) in iter::zip(method_sig.inputs().iter().skip(1), self.fn_sig.inputs()) { - fcx.demand_eqtype(self.call_expr.span, &self_arg_ty, &method_arg_ty); + fcx.demand_eqtype(self.call_expr.span, *self_arg_ty, *method_arg_ty); } fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output()); diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index a397ee771a..56b6c09069 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -328,16 +328,28 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.emit(); } CastError::CastToChar => { - type_error_struct!( + let mut err = type_error_struct!( fcx.tcx.sess, self.span, self.expr_ty, E0604, "only `u8` can be cast as `char`, not `{}`", self.expr_ty - ) - .span_label(self.span, "invalid cast") - .emit(); + ); + err.span_label(self.span, "invalid cast"); + if self.expr_ty.is_numeric() { + err.span_help( + self.span, + if self.expr_ty == fcx.tcx.types.i8 { + "try casting from `u8` instead" + } else if self.expr_ty == fcx.tcx.types.u32 { + "try `char::from_u32` instead" + } else { + "try `char::from_u32` instead (via a `u32`)" + }, + ); + } + err.emit(); } CastError::NonScalar => { let mut err = type_error_struct!( @@ -357,7 +369,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { .try_coerce( self.expr, fcx.tcx.mk_ref( - &ty::RegionKind::ReErased, + fcx.tcx.lifetimes.re_erased, TypeAndMut { ty: expr_ty, mutbl }, ), self.cast_ty, @@ -407,7 +419,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { .try_coerce( self.expr, fcx.tcx.mk_ref( - &ty::RegionKind::ReErased, + fcx.tcx.lifetimes.re_erased, TypeAndMut { ty: self.expr_ty, mutbl }, ), self.cast_ty, @@ -873,7 +885,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { }); // this will report a type mismatch if needed - fcx.demand_eqtype(self.span, ety, m_cast.ty); + fcx.demand_eqtype(self.span, *ety, m_cast.ty); return Ok(CastKind::ArrayPtrCast); } } diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index fd7b3a55df..1650a62f79 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -12,11 +12,12 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{def::Res, ItemKind, Node, PathSegment}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::layout::MAX_SIMD_LANES; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt}; -use rustc_middle::ty::{self, OpaqueTypeKey, ParamEnv, RegionKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, OpaqueTypeKey, ParamEnv, Ty, TyCtxt}; use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_span::symbol::sym; use rustc_span::{self, MultiSpan, Span}; @@ -268,7 +269,7 @@ pub(super) fn check_fn<'a, 'tcx>( ty::Adt(ref adt, _) => { adt.did == panic_info_did && mutbl == hir::Mutability::Not - && *region != RegionKind::ReStatic + && !region.is_static() } _ => false, }, @@ -381,12 +382,17 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b tcx.sess, field_span, E0740, - "unions may not contain fields that need dropping" + "unions cannot contain fields that may need dropping" + ) + .note( + "a type is guaranteed not to need dropping \ + when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type", ) .multipart_suggestion_verbose( - "wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped", + "when the type does not implement `Copy`, \ + wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped", vec![ - (ty_span.shrink_to_lo(), format!("std::mem::ManuallyDrop<")), + (ty_span.shrink_to_lo(), "std::mem::ManuallyDrop<".into()), (ty_span.shrink_to_hi(), ">".into()), ], Applicability::MaybeIncorrect, @@ -462,17 +468,14 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( debug!(?item, ?span); struct FoundParentLifetime; - struct FindParentLifetimeVisitor<'tcx>(TyCtxt<'tcx>, &'tcx ty::Generics); + struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics); impl<'tcx> ty::fold::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> { type BreakTy = FoundParentLifetime; - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.0) - } fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { debug!("FindParentLifetimeVisitor: r={:?}", r); - if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r { - if *index < self.1.parent_count as u32 { + if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r { + if index < self.0.parent_count as u32 { return ControlFlow::Break(FoundParentLifetime); } else { return ControlFlow::CONTINUE; @@ -482,8 +485,8 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( r.super_visit_with(self) } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { - if let ty::ConstKind::Unevaluated(..) = c.val { + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + if let ty::ConstKind::Unevaluated(..) = c.val() { // FIXME(#72219) We currently don't detect lifetimes within substs // which would violate this check. Even though the particular substitution is not used // within the const, this should still be fixed. @@ -502,32 +505,34 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { type BreakTy = Ty<'tcx>; - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t); if t == self.opaque_identity_ty { ControlFlow::CONTINUE } else { - t.super_visit_with(&mut FindParentLifetimeVisitor(self.tcx, self.generics)) + t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics)) .map_break(|FoundParentLifetime| t) } } } impl<'tcx> Visitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { - type Map = rustc_middle::hir::map::Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { - hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { match arg.kind { hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { - [PathSegment { res: Some(Res::SelfTy(_, impl_ref)), .. }] => { + [ + PathSegment { + res: Some(Res::SelfTy { trait_: _, alias_to: impl_ref }), + .. + }, + ] => { let impl_ty_name = impl_ref.map(|(def_id, _)| self.tcx.def_path_str(def_id)); self.selftys.push((path.span, impl_ty_name)); @@ -626,6 +631,7 @@ pub(super) fn check_opaque_for_cycles<'tcx>( /// /// Without this check the above code is incorrectly accepted: we would ICE if /// some tried, for example, to clone an `Option>`. +#[instrument(level = "debug", skip(tcx))] fn check_opaque_meets_bounds<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, @@ -633,17 +639,14 @@ fn check_opaque_meets_bounds<'tcx>( span: Span, origin: &hir::OpaqueTyOrigin, ) { - match origin { - // Checked when type checking the function containing them. - hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => return, - // Can have different predicates to their defining use - hir::OpaqueTyOrigin::TyAlias => {} - } - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let param_env = tcx.param_env(def_id); + let defining_use_anchor = match *origin { + hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did, + hir::OpaqueTyOrigin::TyAlias => def_id, + }; + let param_env = tcx.param_env(defining_use_anchor); - tcx.infer_ctxt().enter(move |infcx| { + tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).enter(move |infcx| { let inh = Inherited::new(infcx, def_id); let infcx = &inh.infcx; let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); @@ -656,16 +659,15 @@ fn check_opaque_meets_bounds<'tcx>( let opaque_type_map = infcx.inner.borrow().opaque_types.clone(); for (OpaqueTypeKey { def_id, substs }, opaque_defn) in opaque_type_map { - match infcx - .at(&misc_cause, param_env) - .eq(opaque_defn.concrete_ty, tcx.type_of(def_id).subst(tcx, substs)) - { + let hidden_type = tcx.type_of(def_id).subst(tcx, substs); + trace!(?hidden_type); + match infcx.at(&misc_cause, param_env).eq(opaque_defn.concrete_ty, hidden_type) { Ok(infer_ok) => inh.register_infer_ok_obligations(infer_ok), Err(ty_err) => tcx.sess.delay_span_bug( - opaque_defn.definition_span, + span, &format!( - "could not unify `{}` with revealed type:\n{}", - opaque_defn.concrete_ty, ty_err, + "could not check bounds on revealed type `{}`:\n{}", + hidden_type, ty_err, ), ), } @@ -678,10 +680,17 @@ fn check_opaque_meets_bounds<'tcx>( infcx.report_fulfillment_errors(&errors, None, false); } - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let fcx = FnCtxt::new(&inh, param_env, hir_id); - fcx.regionck_item(hir_id, span, FxHashSet::default()); + match origin { + // Checked when type checking the function containing them. + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => return, + // Can have different predicates to their defining use + hir::OpaqueTyOrigin::TyAlias => { + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let fcx = FnCtxt::new(&inh, param_env, hir_id); + fcx.regionck_item(hir_id, span, FxHashSet::default()); + } + } }); } @@ -841,14 +850,8 @@ pub(super) fn check_specialization_validity<'tcx>( trait_def: &ty::TraitDef, trait_item: &ty::AssocItem, impl_id: DefId, - impl_item: &hir::ImplItem<'_>, + impl_item: &hir::ImplItemRef, ) { - let kind = match impl_item.kind { - hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, - hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, - }; - let ancestors = match trait_def.ancestors(tcx, impl_id) { Ok(ancestors) => ancestors, Err(_) => return, @@ -857,7 +860,7 @@ pub(super) fn check_specialization_validity<'tcx>( if parent.is_from_trait() { None } else { - Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) + Some((parent, parent.item(tcx, trait_item.def_id))) } }); @@ -894,7 +897,7 @@ pub(super) fn check_specialization_validity<'tcx>( } } -pub(super) fn check_impl_items_against_trait<'tcx>( +fn check_impl_items_against_trait<'tcx>( tcx: TyCtxt<'tcx>, full_impl_span: Span, impl_id: LocalDefId, @@ -926,172 +929,109 @@ 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 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 ty_impl_item = tcx.associated_item(impl_item.def_id); - - 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) + for impl_item in impl_item_refs { + let ty_impl_item = tcx.associated_item(impl_item.id.def_id); + let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id { + tcx.associated_item(trait_item_id) } else { + // Checked in `associated_item`. + tcx.sess.delay_span_bug(impl_item.span, "missing associated item in trait"); continue; }; - - if compatible_kind { - match impl_item.kind { - hir::ImplItemKind::Const(..) => { - // Find associated const definition. - 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); - compare_impl_method( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } - hir::ImplItemKind::TyAlias(impl_ty) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - compare_ty_impl( - tcx, - &ty_impl_item, - impl_ty.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } + let impl_item_full = tcx.hir().impl_item(impl_item.id); + match impl_item_full.kind { + hir::ImplItemKind::Const(..) => { + // Find associated const definition. + 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); + compare_impl_method( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); + } + hir::ImplItemKind::TyAlias(impl_ty) => { + let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); + compare_ty_impl( + tcx, + &ty_impl_item, + impl_ty.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); } - - check_specialization_validity( - tcx, - trait_def, - &ty_trait_item, - 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_specialization_validity( + tcx, + trait_def, + &ty_trait_item, + impl_id.to_def_id(), + impl_item, + ); } 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 mut must_implement_one_of: Option<&[Ident]> = + trait_def.must_implement_one_of.as_deref(); + + for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) { let is_implemented = ancestors - .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.defining_node.is_from_trait()) - .unwrap_or(false); + .leaf_def(tcx, trait_item_id) + .map_or(false, |node_item| node_item.item.defaultness.has_value()); if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { - if !trait_item.defaultness.has_value() { - missing_items.push(*trait_item); + missing_items.push(tcx.associated_item(trait_item_id)); + } + + if let Some(required_items) = &must_implement_one_of { + // true if this item is specifically implemented in this impl + let is_implemented_here = ancestors + .leaf_def(tcx, trait_item_id) + .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); + + if is_implemented_here { + let trait_item = tcx.associated_item(trait_item_id); + if required_items.contains(&trait_item.ident(tcx)) { + must_implement_one_of = None; + } } } } if !missing_items.is_empty() { + let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); 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() - ) - } + if let Some(missing_items) = must_implement_one_of { + let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); + let attr_span = tcx + .get_attrs(impl_trait_ref.def_id) + .iter() + .find(|attr| attr.has_name(sym::rustc_must_implement_one_of)) + .map(|attr| attr.span); - 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() - ) + missing_items_must_implement_one_of_err(tcx, impl_span, missing_items, attr_span); } - }; - - 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 @@ -1271,7 +1211,7 @@ pub(super) fn check_packed_inner( if let ty::Adt(def, _) = field.ty(tcx, substs).kind() { if !stack.contains(&def.did) { if let Some(mut defs) = check_packed_inner(tcx, def.did, stack) { - defs.push((def.did, field.ident.span)); + defs.push((def.did, field.ident(tcx).span)); return Some(defs); } } @@ -1408,7 +1348,7 @@ fn check_enum<'tcx>( let variant_i = tcx.hir().expect_variant(variant_i_hir_id); let i_span = match variant_i.disr_expr { Some(ref expr) => tcx.hir().span(expr.hir_id), - None => tcx.hir().span(variant_i_hir_id), + None => tcx.def_span(variant_did), }; let span = match v.disr_expr { Some(ref expr) => tcx.hir().span(expr.hir_id), @@ -1475,7 +1415,7 @@ pub(super) fn check_type_params_are_used<'tcx>( return; } - for leaf in ty.walk(tcx) { + for leaf in ty.walk() { if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { if let ty::Param(param) = leaf_ty.kind() { debug!("found use of ty param {:?}", param); @@ -1534,8 +1474,8 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) { let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); let mut label = false; - if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) { - let typeck_results = tcx.typeck(tcx.hir().local_def_id(hir_id)); + if let Some((def_id, visitor)) = get_owner_return_paths(tcx, def_id) { + let typeck_results = tcx.typeck(def_id); if visitor .returns .iter() @@ -1573,10 +1513,6 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) { { struct OpaqueTypeCollector(Vec); impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypeCollector { - fn tcx_for_anon_const_substs(&self) -> Option> { - // Default anon const substs cannot contain opaque types. - None - } fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match *t.kind() { ty::Opaque(def, _) => { diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index c87ab0d410..3c626837ef 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -279,7 +279,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let ret_param_ty = projection.skip_binder().ty; + // Since this is a return parameter type it is safe to unwrap. + let ret_param_ty = projection.skip_binder().term.ty().unwrap(); let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty); debug!("deduce_sig_from_projection: ret_param_ty={:?}", ret_param_ty); @@ -449,7 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .skip_binder() .inputs() .iter() - .map(|ty| ArgKind::from_expected_ty(ty, None)) + .map(|ty| ArgKind::from_expected_ty(*ty, None)) .collect(); let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) { Some((sp, args)) => (Some(sp), args), @@ -706,9 +707,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Extract the type from the projection. Note that there can // be no bound variables in this type because the "self type" // does not have any regions in it. - let output_ty = self.resolve_vars_if_possible(predicate.ty); + let output_ty = self.resolve_vars_if_possible(predicate.term); debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty); - Some(output_ty) + // This is a projection on a Fn trait so will always be a type. + Some(output_ty.ty().unwrap()) } /// Converts the types that the user supplied, in case that doing diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 6192c77d6c..be7ac00692 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -142,7 +142,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { where F: FnOnce(Ty<'tcx>) -> Vec>, { - self.unify(&a, &b) + self.unify(a, b) .and_then(|InferOk { value: ty, obligations }| success(f(ty), ty, obligations)) } @@ -472,7 +472,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } }; adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(*r_borrow, mutbl)), target: ty, }); @@ -1575,7 +1575,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expected, found, can_suggest, - fcx.tcx.hir().get_parent_item(id), + fcx.tcx.hir().local_def_id_to_hir_id(fcx.tcx.hir().get_parent_item(id)), ); } if !pointing_at_return_type { @@ -1584,13 +1584,19 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } let parent_id = fcx.tcx.hir().get_parent_item(id); - let parent_item = fcx.tcx.hir().get(parent_id); + let parent_item = fcx.tcx.hir().get_by_def_id(parent_id); if let (Some((expr, _)), Some((fn_decl, _, _))) = (expression, fcx.get_node_fn_decl(parent_item)) { fcx.suggest_missing_break_or_return_expr( - &mut err, expr, fn_decl, expected, found, id, parent_id, + &mut err, + expr, + fn_decl, + expected, + found, + id, + fcx.tcx.hir().local_def_id_to_hir_id(parent_id), ); } @@ -1667,10 +1673,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ], Applicability::MachineApplicable, ); - let sugg = vec![sp, cause.span] + let sugg = [sp, cause.span] .into_iter() .flat_map(|sp| { - vec![ + [ (sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string()), ] diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index c942bafcf0..38449c2a76 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -300,7 +300,7 @@ fn compare_predicate_entailment<'tcx>( cause.span(tcx), E0053, "method `{}` has an incompatible type for trait", - trait_m.ident + trait_m.name ); match &terr { TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) @@ -377,9 +377,9 @@ fn compare_predicate_entailment<'tcx>( &mut diag, &cause, trait_err_span.map(|sp| (sp, "type in trait".to_owned())), - Some(infer::ValuePairs::Types(ExpectedFound { - expected: trait_fty, - found: impl_fty, + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_fty.into(), + found: impl_fty.into(), })), &terr, false, @@ -435,16 +435,24 @@ 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_or(def_span, |g| g.span); + let span = impl_m + .def_id + .as_local() + .and_then(|did| tcx.hir().get_generics(did)) + .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); - tcx.hir().get_generics(trait_m.def_id).map_or(def_sp, |g| g.span) + trait_m + .def_id + .as_local() + .and_then(|did| tcx.hir().get_generics(did)) + .map_or(def_sp, |g| g.span) }); tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { span, item_kind, - ident: impl_m.ident, + ident: impl_m.ident(tcx), generics_span, }); return Err(ErrorReported); @@ -532,14 +540,14 @@ fn compare_self_type<'tcx>( impl_m_span, E0185, "method `{}` has a `{}` declaration in the impl, but not in the trait", - trait_m.ident, + trait_m.name, self_descr ); err.span_label(impl_m_span, format!("`{}` used in impl", self_descr)); if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { err.span_label(span, format!("trait method declared without `{}`", self_descr)); } else { - err.note_trait_signature(trait_m.ident.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); } err.emit(); return Err(ErrorReported); @@ -552,14 +560,14 @@ fn compare_self_type<'tcx>( impl_m_span, E0186, "method `{}` has a `{}` declaration in the trait, but not in the impl", - trait_m.ident, + trait_m.name, self_descr ); err.span_label(impl_m_span, format!("expected `{}` in impl", self_descr)); if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { err.span_label(span, format!("`{}` used in trait", self_descr)); } else { - err.note_trait_signature(trait_m.ident.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); } err.emit(); return Err(ErrorReported); @@ -632,7 +640,7 @@ fn compare_number_of_generics<'tcx>( "{} `{}` has {} {kind} parameter{} but its trait \ declaration has {} {kind} parameter{}", item_kind, - trait_.ident, + trait_.name, impl_count, pluralize!(impl_count), trait_count, @@ -739,7 +747,7 @@ fn compare_number_of_method_arguments<'tcx>( impl_span, E0050, "method `{}` has {} but the declaration in trait `{}` has {}", - trait_m.ident, + trait_m.name, potentially_plural_count(impl_number_args, "parameter"), tcx.def_path_str(trait_m.def_id), trait_number_args @@ -753,7 +761,7 @@ fn compare_number_of_method_arguments<'tcx>( ), ); } else { - err.note_trait_signature(trait_m.ident.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); } err.span_label( impl_span, @@ -803,7 +811,7 @@ fn compare_synthetic_generics<'tcx>( impl_span, E0643, "method `{}` has incompatible signature for trait", - trait_m.ident + trait_m.name ); err.span_label(trait_span, "declaration in trait here"); match (impl_synthetic, trait_synthetic) { @@ -873,13 +881,6 @@ fn compare_synthetic_generics<'tcx>( } } } - type Map = intravisit::ErasedMap<'v>; - fn nested_visit_map( - &mut self, - ) -> intravisit::NestedVisitorMap - { - intravisit::NestedVisitorMap::None - } } let mut visitor = Visitor(None, impl_def_id); for ty in input_tys { @@ -964,7 +965,7 @@ fn compare_const_param_types<'tcx>( *impl_span, E0053, "method `{}` has an incompatible const parameter type for trait", - trait_m.ident + trait_m.name ); err.span_note( trait_span.map_or_else(|| trait_item_span.unwrap_or(*impl_span), |span| *span), @@ -1052,7 +1053,7 @@ crate fn compare_const_impl<'tcx>( cause.span, E0326, "implemented const `{}` has an incompatible type for trait", - trait_c.ident + trait_c.name ); let trait_c_span = trait_c.def_id.as_local().map(|trait_c_def_id| { @@ -1067,9 +1068,9 @@ crate fn compare_const_impl<'tcx>( &mut diag, &cause, trait_c_span.map(|span| (span, "type in trait".to_owned())), - Some(infer::ValuePairs::Types(ExpectedFound { - expected: trait_ty, - found: impl_ty, + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_ty.into(), + found: impl_ty.into(), })), &terr, false, @@ -1305,7 +1306,7 @@ pub fn check_type_bounds<'tcx>( GenericParamDefKind::Const { .. } => { let bound_var = ty::BoundVariableKind::Const; bound_vars.push(bound_var); - tcx.mk_const(ty::Const { + tcx.mk_const(ty::ConstS { ty: tcx.type_of(param.def_id), val: ty::ConstKind::Bound( ty::INNERMOST, @@ -1352,7 +1353,7 @@ pub fn check_type_bounds<'tcx>( item_def_id: trait_ty.def_id, substs: rebased_substs, }, - ty: impl_ty_value, + term: impl_ty_value.into(), }, bound_vars, ) @@ -1377,7 +1378,14 @@ pub fn check_type_bounds<'tcx>( let mut selcx = traits::SelectionContext::new(&infcx); let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); - let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id); + let normalize_cause = ObligationCause::new( + impl_ty_span, + impl_ty_hir_id, + ObligationCauseCode::CheckAssociatedTypeBounds { + impl_item_def_id: impl_ty.def_id, + trait_item_def_id: trait_ty.def_id, + }, + ); let mk_cause = |span: Span| { let code = if span.is_dummy() { traits::MiscObligation diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index b7e276b696..4f180d84e3 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; use super::method::probe; @@ -24,16 +24,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn emit_coerce_suggestions( &self, err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expr_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, error: TypeError<'tcx>, ) { self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_box_deref(err, expr, expected, expr_ty); - self.suggest_compatible_variants(err, expr, expected, expr_ty); self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); + self.suggest_compatible_variants(err, expr, expected, expr_ty); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } @@ -110,7 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn demand_coerce( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -130,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// will be permitted if the diverges flag is currently "always". pub fn demand_coerce_diag( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -259,23 +258,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_box_deref( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - expr_ty: Ty<'tcx>, - ) { - if expr_ty.is_box() && expr_ty.boxed_ty() == expected { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "try dereferencing the `Box`", - "*".to_string(), - Applicability::MachineApplicable, - ); - } - } - /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) fn suggest_compatible_variants( @@ -356,31 +338,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect(); - if let [variant] = &compatible_variants[..] { - // Just a single matching variant. - err.multipart_suggestion( - &format!("try wrapping the expression in `{}`", variant), - vec![ - (expr.span.shrink_to_lo(), format!("{}(", variant)), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } else if compatible_variants.len() > 1 { - // More than one matching variant. - err.multipart_suggestions( - &format!( - "try wrapping the expression in a variant of `{}`", - self.tcx.def_path_str(expected_adt.did) - ), - compatible_variants.into_iter().map(|variant| { + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{}: ", ident), + None => String::new(), + }; + + match &compatible_variants[..] { + [] => { /* No variants to format */ } + [variant] => { + // Just a single matching variant. + err.multipart_suggestion_verbose( + &format!("try wrapping the expression in `{}`", variant), vec![ - (expr.span.shrink_to_lo(), format!("{}(", variant)), + (expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)), (expr.span.shrink_to_hi(), ")".to_string()), - ] - }), - Applicability::MaybeIncorrect, - ); + ], + Applicability::MaybeIncorrect, + ); + } + _ => { + // More than one matching variant. + err.multipart_suggestions( + &format!( + "try wrapping the expression in a variant of `{}`", + self.tcx.def_path_str(expected_adt.did) + ), + compatible_variants.into_iter().map(|variant| { + vec![ + (expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)), + (expr.span.shrink_to_hi(), ")".to_string()), + ] + }), + Applicability::MaybeIncorrect, + ); + } } } } @@ -473,18 +464,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id); let hir = self.tcx.hir().find(expr_parent); let closure_params_len = closure_fn_decl.inputs.len(); - let (method_path, method_span, method_expr) = match (hir, closure_params_len) { + let (method_path, method_expr) = match (hir, closure_params_len) { ( Some(Node::Expr(hir::Expr { - kind: hir::ExprKind::MethodCall(path, span, expr, _), + kind: hir::ExprKind::MethodCall(segment, expr, _), .. })), 1, - ) => (path, span, expr), + ) => (segment, expr), _ => return None, }; - let self_ty = self.typeck_results.borrow().node_type(method_expr[0].hir_id); + let self_ty = self.typeck_results.borrow().expr_ty(&method_expr[0]); let self_ty = format!("{:?}", self_ty); let name = method_path.ident.name; let is_as_ref_able = (self_ty.starts_with("&std::option::Option") @@ -492,42 +483,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self_ty.starts_with("std::option::Option") || self_ty.starts_with("std::result::Result")) && (name == sym::map || name == sym::and_then); - match (is_as_ref_able, self.sess().source_map().span_to_snippet(*method_span)) { + match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) { (true, Ok(src)) => { let suggestion = format!("as_ref().{}", src); - Some((*method_span, "consider using `as_ref` instead", suggestion)) + Some((method_path.ident.span, "consider using `as_ref` instead", suggestion)) } _ => None, } } - crate fn is_hir_id_from_struct_pattern_shorthand_field( + crate fn maybe_get_struct_pattern_shorthand_field( &self, - hir_id: hir::HirId, - sp: Span, - ) -> bool { - let sm = self.sess().source_map(); - let parent_id = self.tcx.hir().get_parent_node(hir_id); - if let Some(parent) = self.tcx.hir().find(parent_id) { - // Account for fields - if let Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) = parent - { - if let Ok(src) = sm.span_to_snippet(sp) { - for field in *fields { - if field.ident.as_str() == src && field.is_shorthand { - return true; - } + expr: &hir::Expr<'_>, + ) -> Option { + let hir = self.tcx.hir(); + let local = match expr { + hir::Expr { + kind: + hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { + res: hir::def::Res::Local(_), + segments: [hir::PathSegment { ident, .. }], + .. + }, + )), + .. + } => Some(ident), + _ => None, + }?; + + match hir.find(hir.get_parent_node(expr.hir_id))? { + Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => { + for field in *fields { + if field.ident.name == local.name && field.is_shorthand { + return Some(local.name); } } } + _ => {} } - false + + None } /// If the given `HirId` corresponds to a block with a trailing expression, return that expression - crate fn maybe_get_block_expr(&self, hir_id: hir::HirId) -> Option<&'tcx hir::Expr<'tcx>> { - match self.tcx.hir().find(hir_id)? { - Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, ..), .. }) => block.expr, + crate fn maybe_get_block_expr(&self, expr: &hir::Expr<'tcx>) -> Option<&'tcx hir::Expr<'tcx>> { + match expr { + hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr, _ => None, } } @@ -565,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// `&mut`!". pub fn check_ref( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, ) -> Option<(Span, &'static str, String, Applicability, bool /* verbose */)> { @@ -583,9 +586,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { s.strip_prefix(old).map(|stripped| new.to_string() + stripped) }; - let is_struct_pat_shorthand_field = - self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp); - // `ExprKind::DropTemps` is semantically irrelevant for these suggestions. let expr = expr.peel_drop_temps(); @@ -643,8 +643,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self.can_coerce(ref_ty, expected) { let mut sugg_sp = sp; - if let hir::ExprKind::MethodCall(ref segment, sp, ref args, _) = expr.kind { - let clone_trait = self.tcx.require_lang_item(LangItem::Clone, Some(sp)); + if let hir::ExprKind::MethodCall(ref segment, ref args, _) = expr.kind { + let clone_trait = + self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span)); if let ([arg], Some(true), sym::clone) = ( &args[..], self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( @@ -679,11 +680,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false, )); } - let field_name = if is_struct_pat_shorthand_field { - format!("{}: ", sugg_expr) - } else { - String::new() + + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{}: ", ident), + None => String::new(), }; + if let Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(left_expr, ..), .. @@ -713,14 +715,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Mutability::Mut => ( sp, "consider mutably borrowing here", - format!("{}&mut {}", field_name, sugg_expr), + format!("{}&mut {}", prefix, sugg_expr), Applicability::MachineApplicable, false, ), hir::Mutability::Not => ( sp, "consider borrowing here", - format!("{}&{}", field_name, sugg_expr), + format!("{}&{}", prefix, sugg_expr), Applicability::MachineApplicable, false, ), @@ -732,7 +734,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr), _, &ty::Ref(_, checked, _), - ) if self.infcx.can_sub(self.param_env, checked, &expected).is_ok() => { + ) if self.infcx.can_sub(self.param_env, checked, expected).is_ok() => { // We have `&T`, check if what was expected was `T`. If so, // we may want to suggest removing a `&`. if sm.is_imported(expr.span) { @@ -857,36 +859,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, false, )); - } else if self.infcx.type_is_copy_modulo_regions( - self.param_env, - expected, - sp, - ) { - // For this suggestion to make sense, the type would need to be `Copy`. - if let Ok(code) = sm.span_to_snippet(expr.span) { - let message = if checked_ty.is_region_ptr() { - "consider dereferencing the borrow" - } else { - "consider dereferencing the type" - }; - let (span, suggestion) = if is_struct_pat_shorthand_field { - (expr.span, format!("{}: *{}", code, code)) - } else if self.is_else_if_block(expr) { - // Don't suggest nonsense like `else *if` - return None; - } else if let Some(expr) = self.maybe_get_block_expr(expr.hir_id) { - (expr.span.shrink_to_lo(), "*".to_string()) - } else { - (expr.span.shrink_to_lo(), "*".to_string()) - }; - return Some(( - span, - message, - suggestion, - Applicability::MachineApplicable, - true, - )); - } + } + + // For this suggestion to make sense, the type would need to be `Copy`, + // or we have to be moving out of a `Box` + if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) + || checked_ty.is_box() + { + let message = if checked_ty.is_box() { + "consider unboxing the value" + } else if checked_ty.is_region_ptr() { + "consider dereferencing the borrow" + } else { + "consider dereferencing the type" + }; + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{}: ", ident), + None => String::new(), + }; + let (span, suggestion) = if self.is_else_if_block(expr) { + // Don't suggest nonsense like `else *if` + return None; + } else if let Some(expr) = self.maybe_get_block_expr(expr) { + // prefix should be empty here.. + (expr.span.shrink_to_lo(), "*".to_string()) + } else { + (expr.span.shrink_to_lo(), format!("{}*", prefix)) + }; + return Some(( + span, + message, + suggestion, + Applicability::MachineApplicable, + true, + )); } } } diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 3cc66aaf0d..22d2022902 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -184,8 +184,6 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // absent. So we report an error that the Drop impl injected a // predicate that is not present on the struct definition. - let self_type_hir_id = tcx.hir().local_def_id_to_hir_id(self_type_did); - // We can assume the predicates attached to struct/enum definition // hold. let generic_assumptions = tcx.predicates_of(self_type_did); @@ -231,7 +229,13 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let p = p.kind(); match (predicate.skip_binder(), p.skip_binder()) { (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => { - relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() + // Since struct predicates cannot have ~const, project the impl predicate + // onto one that ignores the constness. This is equivalent to saying that + // we match a `Trait` bound on the struct with a `Trait` or `~const Trait` + // in the impl. + let non_const_a = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a }; + relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok() } (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() @@ -252,7 +256,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( }; if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) { - let item_span = tcx.hir().span(self_type_hir_id); + let item_span = tcx.def_span(self_type_did); let self_descr = tcx.def_kind(self_type_did).descr(self_type_did.to_def_id()); struct_span_err!( tcx.sess, @@ -358,9 +362,9 @@ impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> { fn consts( &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b); ty::relate::super_relate_consts(self, a, b) } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 621938c9b7..0586351ae1 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -31,10 +31,11 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, QPath}; +use rustc_hir::{ExprKind, HirId, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; +use rustc_middle::middle::stability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts}; @@ -230,8 +231,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we skip issuing a warning because it is autogenerated code. ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"), - ExprKind::MethodCall(_, ref span, _, _) => { - self.warn_if_unreachable(expr.hir_id, *span, "call") + ExprKind::MethodCall(segment, ..) => { + self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call") } _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), } @@ -282,12 +283,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]), ExprKind::InlineAsm(asm) => self.check_expr_asm(asm), - ExprKind::LlvmInlineAsm(asm) => { - for expr in asm.outputs_exprs.iter().chain(asm.inputs_exprs.iter()) { - self.check_expr(expr); - } - tcx.mk_unit() - } ExprKind::Break(destination, ref expr_opt) => { self.check_expr_break(destination, expr_opt.as_deref(), expr) } @@ -312,8 +307,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected), - ExprKind::MethodCall(segment, span, args, _) => { - self.check_method_call(expr, segment, span, args, expected) + ExprKind::MethodCall(segment, args, _) => { + self.check_method_call(expr, segment, args, expected) } ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), ExprKind::Type(e, t) => { @@ -426,9 +421,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Places may legitimately have unsized types. // For example, dereferences of a fat pointer and // the last field of a struct can be unsized. - ExpectHasType(ty) + ExpectHasType(*ty) } else { - Expectation::rvalue_hint(self, ty) + Expectation::rvalue_hint(self, *ty) } } _ => NoExpectation, @@ -742,7 +737,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: hir::ImplItemKind::Fn(..), span: encl_fn_span, .. - })) = self.tcx.hir().find(encl_item_id) + })) = self.tcx.hir().find_by_def_id(encl_item_id) { // We are inside a function body, so reporting "return statement // outside of function body" needs an explanation. @@ -751,7 +746,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this didn't hold, we would not have to report an error in // the first place. - assert_ne!(encl_item_id, encl_body_owner_id); + assert_ne!(hir::HirId::make_owner(encl_item_id), encl_body_owner_id); let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id); let encl_body = self.tcx.hir().body(encl_body_id); @@ -870,14 +865,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), .. }) => { - // We have a situation like `while Some(0) = value.get(0) {`, where `while let` - // was more likely intended. - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "you might have meant to use pattern destructuring", - "let ".to_string(), - Applicability::MachineApplicable, - ); + // Check if our lhs is a child of the condition of a while loop + let expr_is_ancestor = std::iter::successors(Some(lhs.hir_id), |id| { + self.tcx.hir().find_parent_node(*id) + }) + .take_while(|id| *id != parent) + .any(|id| id == expr.hir_id); + // if it is, then we have a situation like `while Some(0) = value.get(0) {`, + // where `while let` was more likely intended. + if expr_is_ancestor { + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern destructuring", + "let ".to_string(), + Applicability::MachineApplicable, + ); + } break; } hir::Node::Item(_) @@ -1103,7 +1106,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &'tcx hir::Expr<'tcx>, segment: &hir::PathSegment<'_>, - span: Span, args: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { @@ -1111,6 +1113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let rcvr_t = self.check_expr(&rcvr); // no need to check for bot/err -- callee does that let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); + let span = segment.ident.span; let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) { Ok(method) => { @@ -1226,7 +1229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let body = self.tcx.hir().body(anon_const.body); // Create a new function context. - let fcx = FnCtxt::new(self, self.param_env, body.value.hir_id); + let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id); crate::check::GatherLocalsVisitor::new(&fcx).visit_body(body); let ty = fcx.check_expr_with_expectation(&body.value, expected); @@ -1315,10 +1318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, ) -> Ty<'tcx> { // Find the relevant variant - let (variant, adt_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, expr.hir_id) - { - variant_ty - } else { + let Some((variant, adt_ty)) = self.check_struct_path(qpath, expr.hir_id) else { self.check_struct_fields_on_error(fields, base_expr); return self.tcx.ty_error(); }; @@ -1376,7 +1376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .fields .iter() .enumerate() - .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) + .map(|(i, field)| (field.ident(tcx).normalize_to_macros_2_0(), (i, field))) .collect::>(); let mut seen_fields = FxHashMap::default(); @@ -1457,7 +1457,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_span, self.field_ty(base_expr.span, f, base_subs), ); - let ident = self.tcx.adjust_ident(f.ident, variant.def_id); + let ident = self + .tcx + .adjust_ident(f.ident(self.tcx), variant.def_id); if let Some(_) = remaining_fields.remove(&ident) { let target_ty = self.field_ty(base_expr.span, f, substs); @@ -1475,10 +1477,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &cause, target_ty, fru_ty, - FieldMisMatch( - variant.ident.name, - ident.name, - ), + FieldMisMatch(variant.name, ident.name), ) .emit(), } @@ -1508,7 +1507,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else { self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { - let base_ty = self.typeck_results.borrow().node_type(base_expr.hir_id); + let base_ty = self.typeck_results.borrow().expr_ty(*base_expr); let same_adt = match (adt_ty.kind(), base_ty.kind()) { (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, _ => false, @@ -1585,10 +1584,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let len = remaining_fields.len(); - let mut displayable_field_names = - remaining_fields.keys().map(|ident| ident.as_str()).collect::>(); - - displayable_field_names.sort(); + let mut displayable_field_names: Vec<&str> = + remaining_fields.keys().map(|ident| ident.as_str()).collect(); + // sorting &str primitives here, sort_unstable is ok + displayable_field_names.sort_unstable(); let mut truncated_fields_error = String::new(); let remaining_fields_names = match &displayable_field_names[..] { @@ -1665,7 +1664,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "{} `{}::{}` has no field named `{}`", kind_name, actual, - variant.ident, + variant.name, field.ident ), _ => struct_span_err!( @@ -1680,15 +1679,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ty, ); + + let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); match variant.ctor_kind { CtorKind::Fn => match ty.kind() { ty::Adt(adt, ..) if adt.is_enum() => { err.span_label( - variant.ident.span, + variant_ident_span, format!( "`{adt}::{variant}` defined here", adt = ty, - variant = variant.ident, + variant = variant.name, ), ); err.span_label(field.ident.span, "field does not exist"); @@ -1697,18 +1698,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &format!( "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", adt = ty, - variant = variant.ident, + variant = variant.name, ), format!( "{adt}::{variant}(/* fields */)", adt = ty, - variant = variant.ident, + variant = variant.name, ), Applicability::HasPlaceholders, ); } _ => { - err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); + err.span_label(variant_ident_span, format!("`{adt}` defined here", adt = ty)); err.span_label(field.ident.span, "field does not exist"); err.span_suggestion_verbose( expr_span, @@ -1725,9 +1726,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { // prevent all specified fields from being suggested let skip_fields = skip_fields.iter().map(|x| x.ident.name); - if let Some(field_name) = - Self::suggest_field_name(variant, field.ident.name, skip_fields.collect()) - { + if let Some(field_name) = self.suggest_field_name( + variant, + field.ident.name, + skip_fields.collect(), + expr_span, + ) { err.span_suggestion( field.ident.span, "a field with a similar name exists", @@ -1740,7 +1744,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if adt.is_enum() { err.span_label( field.ident.span, - format!("`{}::{}` does not have this field", ty, variant.ident), + format!("`{}::{}` does not have this field", ty, variant.name), ); } else { err.span_label( @@ -1748,7 +1752,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("`{}` does not have this field", ty), ); } - let available_field_names = self.available_field_names(variant); + let available_field_names = + self.available_field_names(variant, expr_span); if !available_field_names.is_empty() { err.note(&format!( "available fields are: {}", @@ -1764,23 +1769,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - // Return an hint about the closest match in field names + // Return a hint about the closest match in field names fn suggest_field_name( + &self, variant: &'tcx ty::VariantDef, field: Symbol, skip: Vec, + // The span where stability will be checked + span: Span, ) -> Option { let names = variant .fields .iter() .filter_map(|field| { // ignore already set fields and private fields from non-local crates - if skip.iter().any(|&x| x == field.ident.name) + // and unstable fields. + if skip.iter().any(|&x| x == field.name) || (!variant.def_id.is_local() && !field.vis.is_public()) + || matches!( + self.tcx.eval_stability(field.did, None, span, None), + stability::EvalResult::Deny { .. } + ) { None } else { - Some(field.ident.name) + Some(field.name) } }) .collect::>(); @@ -1788,18 +1801,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { find_best_match_for_name(&names, field, None) } - fn available_field_names(&self, variant: &'tcx ty::VariantDef) -> Vec { + fn available_field_names( + &self, + variant: &'tcx ty::VariantDef, + access_span: Span, + ) -> Vec { variant .fields .iter() .filter(|field| { let def_scope = self .tcx - .adjust_ident_and_get_scope(field.ident, variant.def_id, self.body_id) + .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id) .1; field.vis.is_accessible_from(def_scope, self.tcx) + && !matches!( + self.tcx.eval_stability(field.did, None, access_span, None), + stability::EvalResult::Deny { .. } + ) }) - .map(|field| field.ident.name) + .filter(|field| !self.tcx.is_doc_hidden(field.did)) + .map(|field| field.name) .collect() } @@ -1834,8 +1856,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope(field, base_def.did, self.body_id); let fields = &base_def.non_enum_variant().fields; - if let Some(index) = - fields.iter().position(|f| f.ident.normalize_to_macros_2_0() == ident) + if let Some(index) = fields + .iter() + .position(|f| f.ident(self.tcx).normalize_to_macros_2_0() == ident) { let field = &fields[index]; let field_ty = self.field_ty(expr.span, field, substs); @@ -1913,10 +1936,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => return, }; let mut add_label = true; - if let ty::Adt(def, _) = output_ty.kind() { + if let ty::Adt(def, _) = output_ty.skip_binder().kind() { // no field access on enum type if !def.is_enum() { - if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) { + if def + .non_enum_variant() + .fields + .iter() + .any(|field| field.ident(self.tcx) == field_ident) + { add_label = false; err.span_label( field_ident.span, @@ -1947,7 +1975,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, expr_t); + let mut err = self.no_such_field_err(field, expr_t, base.hir_id); match *expr_t.peel_refs().kind() { ty::Array(_, len) => { @@ -1957,7 +1985,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_first_deref_field(&mut err, expr, base, field); } ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, def, field); + self.suggest_fields_on_recordish(&mut err, def, field, expr.span); } ty::Param(param_ty) => { self.point_at_param_definition(&mut err, param_ty); @@ -2075,7 +2103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap() .fields .iter() - .any(|f| f.ident == field) + .any(|f| f.ident(self.tcx) == field) { if let Some(dot_loc) = expr_snippet.rfind('.') { found = true; @@ -2120,9 +2148,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, def: &'tcx ty::AdtDef, field: Ident, + access_span: Span, ) { if let Some(suggested_field_name) = - Self::suggest_field_name(def.non_enum_variant(), field.name, vec![]) + self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span) { err.span_suggestion( field.span, @@ -2133,7 +2162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_label(field.span, "unknown field"); let struct_variant_def = def.non_enum_variant(); - let field_names = self.available_field_names(struct_variant_def); + let field_names = self.available_field_names(struct_variant_def, access_span); if !field_names.is_empty() { err.note(&format!( "available fields are: {}", @@ -2149,7 +2178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, base: &hir::Expr<'_>, field: Ident, - len: &ty::Const<'tcx>, + len: ty::Const<'tcx>, ) { if let (Some(len), Ok(user_index)) = (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::()) @@ -2184,7 +2213,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn no_such_field_err( &self, field: Ident, - expr_t: &'tcx ty::TyS<'tcx>, + expr_t: Ty<'tcx>, + id: HirId, ) -> DiagnosticBuilder<'_> { let span = field.span; debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t); @@ -2200,11 +2230,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // 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) { + 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![]) - { + if let Some(field_path) = self.check_for_nested_field( + span, + field, + candidate_field, + substs, + vec![], + self.tcx.parent_module(id).to_def_id(), + ) { let field_path_str = field_path .iter() .map(|id| id.name.to_ident_string()) @@ -2256,13 +2291,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidate_field: &ty::FieldDef, subst: SubstsRef<'tcx>, mut field_path: Vec, + id: DefId, ) -> Option> { debug!( "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}", span, candidate_field, field_path ); - if candidate_field.ident == target_field { + if candidate_field.ident(self.tcx) == 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 @@ -2271,14 +2307,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // recursively search fields of `candidate_field` if it's a ty::Adt - field_path.push(candidate_field.ident.normalize_to_macros_2_0()); + field_path.push(candidate_field.ident(self.tcx).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) { + 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 accessible = field.vis.is_accessible_from(id, self.tcx); + if accessible { + let ident = field.ident(self.tcx).normalize_to_macros_2_0(); + if ident == target_field { + return Some(field_path); + } let field_path = field_path.clone(); if let Some(path) = self.check_for_nested_field( span, @@ -2286,6 +2324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field, subst, field_path, + id, ) { return Some(path); } @@ -2407,7 +2446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // allows them to be inferred based on how they are used later in the // function. if is_input { - let ty = self.structurally_resolved_type(expr.span, &ty); + let ty = self.structurally_resolved_type(expr.span, ty); match *ty.kind() { ty::FnDef(..) => { let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs index e5da33d113..7214cdf331 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_typeck/src/check/fallback.rs @@ -70,7 +70,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // unconstrained opaque type variables, in addition to performing // other kinds of fallback. for ty in &self.unsolved_variables() { - fallback_has_occurred |= self.fallback_opaque_type_vars(ty); + fallback_has_occurred |= self.fallback_opaque_type_vars(*ty); } // See if we can make any more progress. @@ -176,7 +176,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .type_var_origin(ty) .map(|origin| origin.span) .unwrap_or(rustc_span::DUMMY_SP); - let oty = self.inner.borrow().opaque_types_vars.get(ty).copied(); + let oty = self.inner.borrow().opaque_types_vars.get(&ty).copied(); if let Some(opaque_ty) = oty { debug!( "fallback_opaque_type_vars(ty={:?}): falling back to opaque type {:?}", diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 1aca291153..2bdc3af90d 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -182,7 +182,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `foo.bar::(...)` -- the `Self` type here will be the // type of `foo` (possibly adjusted), but we don't want to // include that. We want just the `[_, u32]` part. - if !method.substs.is_noop() { + if !method.substs.is_empty() { let method_generics = self.tcx.generics_of(method.def_id); if !method_generics.params.is_empty() { let user_type_annotation = self.infcx.probe(|_| { @@ -211,7 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { - if !substs.is_noop() { + if !substs.is_empty() { debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs); @@ -235,7 +235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { debug!("fcx {}", self.tag()); - if self.can_contain_user_lifetime_bounds((substs, user_self_ty)) { + if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { let canonicalized = self.infcx.canonicalize_user_type_annotation(UserType::TypeOf( def_id, UserSubsts { substs, user_self_ty }, @@ -315,11 +315,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // FIXME: currently we never try to compose autoderefs // and ReifyFnPointer/UnsafeFnPointer, but we could. - _ => bug!( - "while adjusting {:?}, can't compose {:?} and {:?}", - expr, - entry.get(), - adj + _ => self.tcx.sess.delay_span_bug( + expr.span, + &format!( + "while adjusting {:?}, can't compose {:?} and {:?}", + expr, + entry.get(), + adj + ), ), }; *entry.get_mut() = adj; @@ -489,7 +492,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.to_ty(ast_ty); debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); - if self.can_contain_user_lifetime_bounds(ty) { + if Self::can_contain_user_lifetime_bounds(ty) { let c_ty = self.infcx.canonicalize_response(UserType::Ty(ty)); debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); @@ -498,14 +501,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } - pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> &'tcx ty::Const<'tcx> { + pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { match length { &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span), hir::ArrayLen::Body(anon_const) => self.to_const(anon_const), } } - pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> { + pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> { let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); let c = ty::Const::from_anon_const(self.tcx, const_def_id); self.register_wf_obligation( @@ -520,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, ast_c: &hir::AnonConst, param_def_id: DefId, - ) -> &'tcx ty::Const<'tcx> { + ) -> ty::Const<'tcx> { let const_def = ty::WithOptConstParam { did: self.tcx.hir().local_def_id(ast_c.hir_id), const_param_did: Some(param_def_id), @@ -541,11 +544,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // reader, although I have my doubts). Also pass in types with inference // types, because they may be repeated. Other sorts of things are already // sufficiently enforced with erased regions. =) - fn can_contain_user_lifetime_bounds(&self, t: T) -> bool + fn can_contain_user_lifetime_bounds(t: T) -> bool where T: TypeFoldable<'tcx>, { - t.has_free_regions(self.tcx) || t.has_projections() || t.has_infer_types() + t.has_free_regions() || t.has_projections() || t.has_infer_types() } pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { @@ -605,7 +608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: &'tcx ty::FieldDef, substs: SubstsRef<'tcx>, ) -> Ty<'tcx> { - self.normalize_associated_types_in(span, &field.ty(self.tcx, substs)) + self.normalize_associated_types_in(span, field.ty(self.tcx, substs)) } pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { @@ -756,7 +759,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // is polymorphic) and the expected return type. // No argument expectations are produced if unification fails. let origin = self.misc(call_span); - let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret); + let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret); // FIXME(#27336) can't use ? here, Try::from_error doesn't default // to identity so the resulting type is not constrained. @@ -964,7 +967,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if found != self.tcx.types.unit { return; } - if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind { + if let ExprKind::MethodCall(path_segment, [rcvr, ..], _) = expr.kind { if self .typeck_results .borrow() diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index e796fe5817..b69682be8d 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -28,6 +28,11 @@ use crate::structured_errors::StructuredDiagnostic; use std::iter; use std::slice; +struct FnArgsAsTuple<'hir> { + first: &'hir hir::Expr<'hir>, + last: &'hir hir::Expr<'hir>, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&self) { let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); @@ -127,136 +132,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expected_arg_count = formal_input_tys.len(); - let param_count_error = |expected_count: usize, - arg_count: usize, - error_code: &str, - c_variadic: bool, - sugg_unit: bool| { - let (span, start_span, args, ctor_of) = match &call_expr.kind { - hir::ExprKind::Call( - hir::Expr { - span, - kind: - hir::ExprKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. }, - )), - .. - }, - args, - ) => (*span, *span, &args[..], Some(of)), - hir::ExprKind::Call(hir::Expr { span, .. }, args) => { - (*span, *span, &args[..], None) - } - hir::ExprKind::MethodCall(path_segment, span, args, _) => ( - *span, - // `sp` doesn't point at the whole `foo.bar()`, only at `bar`. - path_segment - .args - .and_then(|args| args.args.iter().last()) - // Account for `foo.bar::()`. - .map(|arg| { - // Skip the closing `>`. - tcx.sess - .source_map() - .next_point(tcx.sess.source_map().next_point(arg.span())) - }) - .unwrap_or(*span), - &args[1..], // Skip the receiver. - None, // methods are never ctors - ), - k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), - }; - let arg_spans = if provided_args.is_empty() { - // foo() - // ^^^-- supplied 0 arguments - // | - // expected 2 arguments - vec![tcx.sess.source_map().next_point(start_span).with_hi(call_span.hi())] - } else { - // foo(1, 2, 3) - // ^^^ - - - supplied 3 arguments - // | - // expected 2 arguments - args.iter().map(|arg| arg.span).collect::>() - }; - - let mut err = tcx.sess.struct_span_err_with_code( - span, - &format!( - "this {} takes {}{} but {} {} supplied", - match ctor_of { - Some(CtorOf::Struct) => "struct", - Some(CtorOf::Variant) => "enum variant", - None => "function", - }, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(expected_count, "argument"), - potentially_plural_count(arg_count, "argument"), - if arg_count == 1 { "was" } else { "were" } - ), - DiagnosticId::Error(error_code.to_owned()), - ); - let label = format!("supplied {}", potentially_plural_count(arg_count, "argument")); - for (i, span) in arg_spans.into_iter().enumerate() { - err.span_label( - span, - if arg_count == 0 || i + 1 == arg_count { &label } else { "" }, - ); - } - - if let Some(def_id) = fn_def_id { - 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); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } - } - - if sugg_unit { - let sugg_span = tcx.sess.source_map().end_point(call_expr.span); - // remove closing `)` from the span - let sugg_span = sugg_span.shrink_to_lo(); - err.span_suggestion( - sugg_span, - "expected the unit value `()`; create it with empty parentheses", - String::from("()"), - Applicability::MachineApplicable, - ); - } else { - err.span_label( - span, - format!( - "expected {}{}", - if c_variadic { "at least " } else { "" }, - potentially_plural_count(expected_count, "argument") - ), - ); - } - err.emit(); - }; + // expected_count, arg_count, error_code, sugg_unit, sugg_tuple_wrap_args + let mut arg_count_error: Option<(usize, usize, &str, bool, Option>)> = + None; + // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]); match tuple_type.kind() { - ty::Tuple(arg_types) if arg_types.len() != provided_args.len() => { - param_count_error(arg_types.len(), provided_args.len(), "E0057", false, false); - (self.err_args(provided_args.len()), vec![]) - } + // We expected a tuple and got a tuple ty::Tuple(arg_types) => { + // Argument length differs + if arg_types.len() != provided_args.len() { + arg_count_error = + Some((arg_types.len(), provided_args.len(), "E0057", false, None)); + } let expected_input_tys = match expected_input_tys.get(0) { Some(&ty) => match ty.kind() { ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), @@ -267,6 +157,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (arg_types.iter().map(|k| k.expect_ty()).collect(), expected_input_tys) } _ => { + // Otherwise, there's a mismatch, so clear out what we're expecting, and set + // our input typs to err_args so we don't blow up the error messages struct_span_err!( tcx.sess, call_span, @@ -284,7 +176,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if supplied_arg_count >= expected_arg_count { (formal_input_tys.to_vec(), expected_input_tys) } else { - param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); + arg_count_error = + Some((expected_arg_count, supplied_arg_count, "E0060", false, None)); (self.err_args(supplied_arg_count), vec![]) } } else { @@ -296,8 +189,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { false }; - param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); + // are we passing elements of a tuple without the tuple parentheses? + let expected_input_tys = if expected_input_tys.is_empty() { + // In most cases we can use expected_input_tys, but some callers won't have the type + // information, in which case we fall back to the types from the input expressions. + formal_input_tys + } else { + &*expected_input_tys + }; + + let sugg_tuple_wrap_args = self.suggested_tuple_wrap(expected_input_tys, provided_args); + + arg_count_error = Some(( + expected_arg_count, + supplied_arg_count, + "E0061", + sugg_unit, + sugg_tuple_wrap_args, + )); (self.err_args(supplied_arg_count), vec![]) }; @@ -315,13 +225,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert_eq!(expected_input_tys.len(), formal_input_tys.len()); + let provided_arg_count: usize = provided_args.len(); + // Keep track of the fully coerced argument types - let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; + let mut final_arg_types: Vec, Ty<'_>)>> = vec![None; provided_arg_count]; // We introduce a helper function to demand that a given argument satisfy a given input // This is more complicated than just checking type equality, as arguments could be coerced // This version writes those types back so further type checking uses the narrowed types - let demand_compatible = |idx, final_arg_types: &mut Vec<(usize, Ty<'tcx>, Ty<'tcx>)>| { + let demand_compatible = |idx, final_arg_types: &mut Vec, Ty<'tcx>)>>| { let formal_input_ty: Ty<'tcx> = formal_input_tys[idx]; let expected_input_ty: Ty<'tcx> = expected_input_tys[idx]; let provided_arg = &provided_args[idx]; @@ -340,13 +252,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); // Keep track of these for below - final_arg_types.push((idx, checked_ty, coerced_ty)); + final_arg_types[idx] = Some((checked_ty, coerced_ty)); // Cause selection errors caused by resolving a single argument to point at the // argument and not the call. This is otherwise redundant with the `demand_coerce` // call immediately after, but it lets us customize the span pointed to in the // fulfillment error to be more accurate. - let _ = + let coerced_ty = self.resolve_vars_with_obligations_and_mutate_fulfillment(coerced_ty, |errors| { self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); self.point_at_arg_instead_of_call_if_possible( @@ -358,6 +270,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); }); + final_arg_types[idx] = Some((checked_ty, coerced_ty)); + // We're processing function arguments so we definitely want to use // two-phase borrows. self.demand_coerce(&provided_arg, checked_ty, coerced_ty, None, AllowTwoPhase::Yes); @@ -416,6 +330,134 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + // If there was an error in parameter count, emit that here + if let Some((expected_count, arg_count, err_code, sugg_unit, sugg_tuple_wrap_args)) = + arg_count_error + { + let (span, start_span, args, ctor_of) = match &call_expr.kind { + hir::ExprKind::Call( + hir::Expr { + span, + kind: + hir::ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: Res::Def(DefKind::Ctor(of, _), _), .. }, + )), + .. + }, + args, + ) => (*span, *span, &args[..], Some(of)), + hir::ExprKind::Call(hir::Expr { span, .. }, args) => { + (*span, *span, &args[..], None) + } + hir::ExprKind::MethodCall(path_segment, args, _) => ( + path_segment.ident.span, + // `sp` doesn't point at the whole `foo.bar()`, only at `bar`. + path_segment + .args + .and_then(|args| args.args.iter().last()) + // Account for `foo.bar::()`. + .map(|arg| { + // Skip the closing `>`. + tcx.sess + .source_map() + .next_point(tcx.sess.source_map().next_point(arg.span())) + }) + .unwrap_or(path_segment.ident.span), + &args[1..], // Skip the receiver. + None, // methods are never ctors + ), + k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), + }; + let arg_spans = if provided_args.is_empty() { + // foo() + // ^^^-- supplied 0 arguments + // | + // expected 2 arguments + vec![tcx.sess.source_map().next_point(start_span).with_hi(call_span.hi())] + } else { + // foo(1, 2, 3) + // ^^^ - - - supplied 3 arguments + // | + // expected 2 arguments + args.iter().map(|arg| arg.span).collect::>() + }; + let call_name = match ctor_of { + Some(CtorOf::Struct) => "struct", + Some(CtorOf::Variant) => "enum variant", + None => "function", + }; + let mut err = tcx.sess.struct_span_err_with_code( + span, + &format!( + "this {} takes {}{} but {} {} supplied", + call_name, + if c_variadic { "at least " } else { "" }, + potentially_plural_count(expected_count, "argument"), + potentially_plural_count(arg_count, "argument"), + if arg_count == 1 { "was" } else { "were" } + ), + DiagnosticId::Error(err_code.to_owned()), + ); + let label = format!("supplied {}", potentially_plural_count(arg_count, "argument")); + for (i, span) in arg_spans.into_iter().enumerate() { + err.span_label( + span, + if arg_count == 0 || i + 1 == arg_count { &label } else { "" }, + ); + } + if let Some(def_id) = fn_def_id { + 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); + err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + } + } + if sugg_unit { + let sugg_span = tcx.sess.source_map().end_point(call_expr.span); + // remove closing `)` from the span + let sugg_span = sugg_span.shrink_to_lo(); + err.span_suggestion( + sugg_span, + "expected the unit value `()`; create it with empty parentheses", + String::from("()"), + Applicability::MachineApplicable, + ); + } else if let Some(FnArgsAsTuple { first, last }) = sugg_tuple_wrap_args { + err.multipart_suggestion( + "use parentheses to construct a tuple", + vec![ + (first.span.shrink_to_lo(), '('.to_string()), + (last.span.shrink_to_hi(), ')'.to_string()), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_label( + span, + format!( + "expected {}{}", + if c_variadic { "at least " } else { "" }, + potentially_plural_count(expected_count, "argument") + ), + ); + } + err.emit(); + } + // We also need to make sure we at least write the ty of the other // arguments which we skipped above. if c_variadic { @@ -452,6 +494,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn suggested_tuple_wrap( + &self, + expected_input_tys: &[Ty<'tcx>], + provided_args: &'tcx [hir::Expr<'tcx>], + ) -> Option> { + let [expected_arg_type] = &expected_input_tys[..] else { return None }; + + let ty::Tuple(expected_elems) = self.resolve_vars_if_possible(*expected_arg_type).kind() + else { return None }; + + let expected_types: Vec<_> = expected_elems.iter().map(|k| k.expect_ty()).collect(); + let supplied_types: Vec<_> = provided_args.iter().map(|arg| self.check_expr(arg)).collect(); + + let all_match = iter::zip(expected_types, supplied_types) + .all(|(expected, supplied)| self.can_eq(self.param_env, expected, supplied).is_ok()); + + if all_match { + match provided_args { + [] => None, + [_] => unreachable!( + "shouldn't reach here - need count mismatch between 1-tuple and 1-argument" + ), + [first, .., last] => Some(FnArgsAsTuple { first, last }), + } + } else { + None + } + } + // AST fragment checking pub(in super::super) fn check_lit( &self, @@ -511,7 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => bug!("unexpected type: {:?}", ty), }, Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) - | Res::SelfTy(..) => match ty.kind() { + | Res::SelfTy { .. } => match ty.kind() { ty::Adt(adt, substs) if !adt.is_enum() => { Some((adt.non_enum_variant(), adt.did, substs)) } @@ -846,7 +917,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn parent_item_span(&self, id: hir::HirId) -> Option { - let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id)); + let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id)); match node { Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { @@ -862,7 +933,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id)); + let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id)); self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) } @@ -975,7 +1046,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn point_at_arg_instead_of_call_if_possible( &self, errors: &mut Vec>, - final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)], + final_arg_types: &[Option<(Ty<'tcx>, Ty<'tcx>)>], expr: &'tcx hir::Expr<'tcx>, call_sp: Span, args: &'tcx [hir::Expr<'tcx>], @@ -1016,7 +1087,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ObligationCauseCode::BuiltinDerivedObligation(code) | ObligationCauseCode::ImplDerivedObligation(code) | ObligationCauseCode::DerivedObligation(code) => { - code.parent_trait_ref.self_ty().skip_binder().into() + code.parent_trait_pred.self_ty().skip_binder().into() } _ if let ty::PredicateKind::Trait(predicate) = error.obligation.predicate.kind().skip_binder() => { @@ -1030,13 +1101,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `FulfillmentError`. let mut referenced_in = final_arg_types .iter() - .map(|&(i, checked_ty, _)| (i, checked_ty)) - .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty))) + .enumerate() + .filter_map(|(i, arg)| match arg { + Some((checked_ty, coerce_ty)) => Some([(i, *checked_ty), (i, *coerce_ty)]), + _ => None, + }) + .flatten() .flat_map(|(i, ty)| { let ty = self.resolve_vars_if_possible(ty); // We walk the argument type because the argument's type could have // been `Option`, but the `FulfillmentError` references `T`. - if ty.walk(self.tcx).any(|arg| arg == self_) { Some(i) } else { None } + if ty.walk().any(|arg| arg == self_) { Some(i) } else { None } }) .collect::>(); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs index 9c70d2cb36..222c14d0d4 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs @@ -188,8 +188,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { ) -> ty::GenericPredicates<'tcx> { let tcx = self.tcx; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let item_id = tcx.hir().ty_param_owner(hir_id); - let item_def_id = tcx.hir().local_def_id(item_id); + let item_def_id = tcx.hir().ty_param_owner(hir_id); let generics = tcx.generics_of(item_def_id); let index = generics.param_def_id_to_index[&def_id]; ty::GenericPredicates { @@ -240,7 +239,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, param: Option<&ty::GenericParamDef>, span: Span, - ) -> &'tcx Const<'tcx> { + ) -> Const<'tcx> { if let Some(param) = param { if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { return ct; @@ -283,7 +282,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { if ty.has_escaping_bound_vars() { ty // FIXME: normalization and escaping regions } else { - self.normalize_associated_types_in(span, &ty) + self.normalize_associated_types_in(span, ty) } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 6c7d3a0c9c..f9c482713f 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -8,8 +8,12 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; -use rustc_hir::{Expr, ExprKind, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind}; -use rustc_infer::infer; +use rustc_hir::{ + Expr, ExprKind, GenericBound, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind, + WherePredicate, +}; +use rustc_infer::infer::{self, TyCtxtInferExt}; + use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, Ty}; use rustc_span::symbol::{kw, sym}; @@ -208,7 +212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn suggest_deref_ref_or_into( &self, err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -231,13 +235,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { let is_struct_pat_shorthand_field = - self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span); + self.maybe_get_struct_pattern_shorthand_field(expr).is_some(); let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods) .filter_map(|(receiver, method)| { - let method_call = format!(".{}()", method.ident); + let method_call = format!(".{}()", method.name); if receiver.ends_with(&method_call) { None // do not suggest code that is already there (#53348) } else { @@ -559,6 +563,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.tcx.erase_late_bound_regions(ty); if self.can_coerce(expected, ty) { err.span_label(sp, format!("expected `{}` because of return type", expected)); + self.try_suggest_return_impl_trait(err, expected, ty, fn_id); return true; } false @@ -566,6 +571,115 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// check whether the return type is a generic type with a trait bound + /// only suggest this if the generic param is not present in the arguments + /// if this is true, hint them towards changing the return type to `impl Trait` + /// ``` + /// fn cant_name_it u32>() -> T { + /// || 3 + /// } + /// ``` + fn try_suggest_return_impl_trait( + &self, + err: &mut DiagnosticBuilder<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + fn_id: hir::HirId, + ) { + // Only apply the suggestion if: + // - the return type is a generic parameter + // - the generic param is not used as a fn param + // - the generic param has at least one bound + // - the generic param doesn't appear in any other bounds where it's not the Self type + // Suggest: + // - Changing the return type to be `impl ` + + debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found); + + let ty::Param(expected_ty_as_param) = expected.kind() else { return }; + + let fn_node = self.tcx.hir().find(fn_id); + + let Some(hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Fn( + hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. }, + hir::Generics { params, where_clause, .. }, + _body_id, + ), + .. + })) = fn_node else { return }; + + let Some(expected_generic_param) = params.get(expected_ty_as_param.index as usize) else { return }; + + // get all where BoundPredicates here, because they are used in to cases below + let where_predicates = where_clause + .predicates + .iter() + .filter_map(|p| match p { + WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + bounds, + bounded_ty, + .. + }) => { + // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) + let ty = >::ast_ty_to_ty(self, bounded_ty); + Some((ty, bounds)) + } + _ => None, + }) + .map(|(ty, bounds)| match ty.kind() { + ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)), + // check whether there is any predicate that contains our `T`, like `Option: Send` + _ => match ty.contains(expected) { + true => Err(()), + false => Ok(None), + }, + }) + .collect::, _>>(); + + let Ok(where_predicates) = where_predicates else { return }; + + // now get all predicates in the same types as the where bounds, so we can chain them + let predicates_from_where = + where_predicates.iter().flatten().map(|bounds| bounds.iter()).flatten(); + + // extract all bounds from the source code using their spans + let all_matching_bounds_strs = expected_generic_param + .bounds + .iter() + .chain(predicates_from_where) + .filter_map(|bound| match bound { + GenericBound::Trait(_, _) => { + self.tcx.sess.source_map().span_to_snippet(bound.span()).ok() + } + _ => None, + }) + .collect::>(); + + if all_matching_bounds_strs.len() == 0 { + return; + } + + let all_bounds_str = all_matching_bounds_strs.join(" + "); + + let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { + let ty = >::ast_ty_to_ty(self, param); + matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) + }); + + if ty_param_used_in_fn_params { + return; + } + + err.span_suggestion( + fn_return.span(), + "consider using an impl return type", + format!("impl {}", all_bounds_str), + Applicability::MaybeIncorrect, + ); + } + pub(in super::super) fn suggest_missing_break_or_return_expr( &self, err: &mut DiagnosticBuilder<'_>, @@ -608,6 +722,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = self.normalize_associated_types_in(expr.span, ty); + let ty = match self.tcx.asyncness(fn_id.owner) { + hir::IsAsync::Async => self + .tcx + .infer_ctxt() + .enter(|infcx| { + infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| { + span_bug!( + fn_decl.output.span(), + "failed to get output type of async function" + ) + }) + }) + .skip_binder(), + hir::IsAsync::NotAsync => ty, + }; if self.can_coerce(found, ty) { err.multipart_suggestion( "you might have meant to return this value", diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs index 839bd56b39..576dc6f127 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -1,6 +1,6 @@ use crate::check::{FnCtxt, LocalTy, UserType}; use rustc_hir as hir; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::PatKind; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::Ty; @@ -92,18 +92,12 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { debug!( "local variable {:?} is assigned type {}", decl.pat, - self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty) + self.fcx.ty_to_string(self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty) ); } } impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - // Add explicitly-declared locals. fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.declare(local.into()); @@ -143,7 +137,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { debug!( "pattern binding {} is assigned to {} with type {:?}", ident, - self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty), + self.fcx.ty_to_string(self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty), var_ty ); } diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index d54b1d62ee..d360f34ae7 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -3,6 +3,7 @@ //! is calculated in `rustc_const_eval::transform::generator` and may be a subset of the //! types computed here. +use self::drop_ranges::DropRanges; use super::FnCtxt; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::pluralize; @@ -10,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdSet; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; use rustc_middle::middle::region::{self, YieldData}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -19,6 +20,8 @@ use rustc_span::Span; use smallvec::SmallVec; use tracing::debug; +mod drop_ranges; + struct InteriorVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, types: FxIndexSet>, @@ -34,6 +37,7 @@ struct InteriorVisitor<'a, 'tcx> { guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>, guard_bindings_set: HirIdSet, linted_values: HirIdSet, + drop_ranges: DropRanges, } impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { @@ -48,9 +52,11 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { ) { use rustc_span::DUMMY_SP; + let ty = self.fcx.resolve_vars_if_possible(ty); + debug!( - "generator_interior: attempting to record type {:?} {:?} {:?} {:?}", - ty, scope, expr, source_span + "attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}", + ty, hir_id, scope, expr, source_span, self.expr_count, ); let live_across_yield = scope @@ -63,21 +69,30 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { // // See the mega-comment at `yield_in_scope` for a proof. - debug!( - "comparing counts yield: {} self: {}, source_span = {:?}", - yield_data.expr_and_pat_count, self.expr_count, source_span - ); + yield_data + .iter() + .find(|yield_data| { + debug!( + "comparing counts yield: {} self: {}, source_span = {:?}", + yield_data.expr_and_pat_count, self.expr_count, source_span + ); + + if self.fcx.sess().opts.debugging_opts.drop_tracking + && self + .drop_ranges + .is_dropped_at(hir_id, yield_data.expr_and_pat_count) + { + debug!("value is dropped at yield point; not recording"); + return false; + } - // If it is a borrowing happening in the guard, - // it needs to be recorded regardless because they - // do live across this yield point. - if guard_borrowing_from_pattern - || yield_data.expr_and_pat_count >= self.expr_count - { - Some(yield_data) - } else { - None - } + // If it is a borrowing happening in the guard, + // it needs to be recorded regardless because they + // do live across this yield point. + guard_borrowing_from_pattern + || yield_data.expr_and_pat_count >= self.expr_count + }) + .cloned() }) }) .unwrap_or_else(|| { @@ -85,7 +100,6 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { }); if let Some(yield_data) = live_across_yield { - let ty = self.fcx.resolve_vars_if_possible(ty); debug!( "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}", expr, scope, ty, self.expr_count, yield_data.span @@ -141,7 +155,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { self.types.insert(ty::GeneratorInteriorTypeCause { span: source_span, - ty: &ty, + ty, scope_span, yield_span: yield_data.span, expr: expr.map(|e| e.hir_id), @@ -154,7 +168,6 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { self.expr_count, expr.map(|e| e.span) ); - let ty = self.fcx.resolve_vars_if_possible(ty); if let Some((unresolved_type, unresolved_type_span)) = self.fcx.unresolved_type_vars(&ty) { @@ -186,10 +199,11 @@ pub fn resolve_interior<'a, 'tcx>( guard_bindings: <_>::default(), guard_bindings_set: <_>::default(), linted_values: <_>::default(), + drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body), }; intravisit::walk_body(&mut visitor, body); - // Check that we visited the same amount of expressions and the RegionResolutionVisitor + // Check that we visited the same amount of expressions as the RegionResolutionVisitor let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap(); assert_eq!(region_expr_count, visitor.expr_count); @@ -266,12 +280,6 @@ pub fn resolve_interior<'a, 'tcx>( // librustc_middle/middle/region.rs since `expr_count` is compared against the results // there. impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) { let Arm { guard, pat, body, .. } = arm; self.visit_pat(pat); @@ -319,6 +327,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { let mut guard_borrowing_from_pattern = false; + match &expr.kind { ExprKind::Call(callee, args) => match &callee.kind { ExprKind::Path(qpath) => { @@ -407,7 +416,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { let tcx = self.fcx.tcx; let ref_ty = tcx.mk_ref( // Use `ReErased` as `resolve_interior` is going to replace all the regions anyway. - tcx.mk_region(ty::RegionKind::ReErased), + tcx.mk_region(ty::ReErased), ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, ); self.record( @@ -439,12 +448,6 @@ struct ArmPatCollector<'a> { } impl<'a, 'tcx> Visitor<'tcx> for ArmPatCollector<'a> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { intravisit::walk_pat(self, pat); if let PatKind::Binding(_, id, ..) = pat.kind { diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs new file mode 100644 index 0000000000..972dd622d6 --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs @@ -0,0 +1,279 @@ +//! Drop range analysis finds the portions of the tree where a value is guaranteed to be dropped +//! (i.e. moved, uninitialized, etc.). This is used to exclude the types of those values from the +//! generator type. See `InteriorVisitor::record` for where the results of this analysis are used. +//! +//! There are three phases to this analysis: +//! 1. Use `ExprUseVisitor` to identify the interesting values that are consumed and borrowed. +//! 2. Use `DropRangeVisitor` to find where the interesting values are dropped or reinitialized, +//! and also build a control flow graph. +//! 3. Use `DropRanges::propagate_to_fixpoint` to flow the dropped/reinitialized information through +//! the CFG and find the exact points where we know a value is definitely dropped. +//! +//! The end result is a data structure that maps the post-order index of each node in the HIR tree +//! to a set of values that are known to be dropped at that location. + +use self::cfg_build::build_control_flow_graph; +use self::record_consumed_borrow::find_consumed_and_borrowed; +use crate::check::FnCtxt; +use hir::def_id::DefId; +use hir::{Body, HirId, HirIdMap, Node}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::hir::map::Map; +use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; +use rustc_middle::ty; +use std::collections::BTreeMap; +use std::fmt::Debug; + +mod cfg_build; +mod cfg_propagate; +mod cfg_visualize; +mod record_consumed_borrow; + +pub fn compute_drop_ranges<'a, 'tcx>( + fcx: &'a FnCtxt<'a, 'tcx>, + def_id: DefId, + body: &'tcx Body<'tcx>, +) -> DropRanges { + if fcx.sess().opts.debugging_opts.drop_tracking { + let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body); + + let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0); + let mut drop_ranges = build_control_flow_graph( + fcx.tcx.hir(), + fcx.tcx, + &fcx.typeck_results.borrow(), + consumed_borrowed_places, + body, + num_exprs, + ); + + drop_ranges.propagate_to_fixpoint(); + + DropRanges { tracked_value_map: drop_ranges.tracked_value_map, nodes: drop_ranges.nodes } + } else { + // If drop range tracking is not enabled, skip all the analysis and produce an + // empty set of DropRanges. + DropRanges { tracked_value_map: FxHashMap::default(), nodes: IndexVec::new() } + } +} + +/// Applies `f` to consumable node in the HIR subtree pointed to by `place`. +/// +/// This includes the place itself, and if the place is a reference to a local +/// variable then `f` is also called on the HIR node for that variable as well. +/// +/// For example, if `place` points to `foo()`, then `f` is called once for the +/// result of `foo`. On the other hand, if `place` points to `x` then `f` will +/// be called both on the `ExprKind::Path` node that represents the expression +/// as well as the HirId of the local `x` itself. +fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) { + f(place); + let node = hir.find(place.hir_id()); + if let Some(Node::Expr(expr)) = node { + match expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) => { + f(TrackedValue::Variable(*hir_id)); + } + _ => (), + } + } +} + +rustc_index::newtype_index! { + pub struct PostOrderId { + DEBUG_FORMAT = "id({})", + } +} + +rustc_index::newtype_index! { + pub struct TrackedValueIndex { + DEBUG_FORMAT = "hidx({})", + } +} + +/// Identifies a value whose drop state we need to track. +#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)] +enum TrackedValue { + /// Represents a named variable, such as a let binding, parameter, or upvar. + /// + /// The HirId points to the variable's definition site. + Variable(HirId), + /// A value produced as a result of an expression. + /// + /// The HirId points to the expression that returns this value. + Temporary(HirId), +} + +impl TrackedValue { + fn hir_id(&self) -> HirId { + match self { + TrackedValue::Variable(hir_id) | TrackedValue::Temporary(hir_id) => *hir_id, + } + } + + fn from_place_with_projections_allowed(place_with_id: &PlaceWithHirId<'_>) -> Self { + match place_with_id.place.base { + PlaceBase::Rvalue | PlaceBase::StaticItem => { + TrackedValue::Temporary(place_with_id.hir_id) + } + PlaceBase::Local(hir_id) + | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => { + TrackedValue::Variable(hir_id) + } + } + } +} + +/// Represents a reason why we might not be able to convert a HirId or Place +/// into a tracked value. +#[derive(Debug)] +enum TrackedValueConversionError { + /// Place projects are not currently supported. + /// + /// The reasoning around these is kind of subtle, so we choose to be more + /// conservative around these for now. There is not reason in theory we + /// cannot support these, we just have not implemented it yet. + PlaceProjectionsNotSupported, +} + +impl TryFrom<&PlaceWithHirId<'_>> for TrackedValue { + type Error = TrackedValueConversionError; + + fn try_from(place_with_id: &PlaceWithHirId<'_>) -> Result { + if !place_with_id.place.projections.is_empty() { + debug!( + "TrackedValue from PlaceWithHirId: {:?} has projections, which are not supported.", + place_with_id + ); + return Err(TrackedValueConversionError::PlaceProjectionsNotSupported); + } + + Ok(TrackedValue::from_place_with_projections_allowed(place_with_id)) + } +} + +pub struct DropRanges { + tracked_value_map: FxHashMap, + nodes: IndexVec, +} + +impl DropRanges { + pub fn is_dropped_at(&self, hir_id: HirId, location: usize) -> bool { + self.tracked_value_map + .get(&TrackedValue::Temporary(hir_id)) + .or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id))) + .cloned() + .map_or(false, |tracked_value_id| { + self.expect_node(location.into()).drop_state.contains(tracked_value_id) + }) + } + + /// Returns a reference to the NodeInfo for a node, panicking if it does not exist + fn expect_node(&self, id: PostOrderId) -> &NodeInfo { + &self.nodes[id] + } +} + +/// Tracks information needed to compute drop ranges. +struct DropRangesBuilder { + /// The core of DropRangesBuilder is a set of nodes, which each represent + /// one expression. We primarily refer to them by their index in a + /// post-order traversal of the HIR tree, since this is what + /// generator_interior uses to talk about yield positions. + /// + /// This IndexVec keeps the relevant details for each node. See the + /// NodeInfo struct for more details, but this information includes things + /// such as the set of control-flow successors, which variables are dropped + /// or reinitialized, and whether each variable has been inferred to be + /// known-dropped or potentially reintiialized at each point. + nodes: IndexVec, + /// We refer to values whose drop state we are tracking by the HirId of + /// where they are defined. Within a NodeInfo, however, we store the + /// drop-state in a bit vector indexed by a HirIdIndex + /// (see NodeInfo::drop_state). The hir_id_map field stores the mapping + /// from HirIds to the HirIdIndex that is used to represent that value in + /// bitvector. + tracked_value_map: FxHashMap, + + /// When building the control flow graph, we don't always know the + /// post-order index of the target node at the point we encounter it. + /// For example, this happens with break and continue. In those cases, + /// we store a pair of the PostOrderId of the source and the HirId + /// of the target. Once we have gathered all of these edges, we make a + /// pass over the set of deferred edges (see process_deferred_edges in + /// cfg_build.rs), look up the PostOrderId for the target (since now the + /// post-order index for all nodes is known), and add missing control flow + /// edges. + deferred_edges: Vec<(PostOrderId, HirId)>, + /// This maps HirIds of expressions to their post-order index. It is + /// used in process_deferred_edges to correctly add back-edges. + post_order_map: HirIdMap, +} + +impl Debug for DropRangesBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DropRanges") + .field("hir_id_map", &self.tracked_value_map) + .field("post_order_maps", &self.post_order_map) + .field("nodes", &self.nodes.iter_enumerated().collect::>()) + .finish() + } +} + +/// DropRanges keeps track of what values are definitely dropped at each point in the code. +/// +/// Values of interest are defined by the hir_id of their place. Locations in code are identified +/// by their index in the post-order traversal. At its core, DropRanges maps +/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely +/// dropped at the point of the node identified by post_order_id. +impl DropRangesBuilder { + /// Returns the number of values (hir_ids) that are tracked + fn num_values(&self) -> usize { + self.tracked_value_map.len() + } + + fn node_mut(&mut self, id: PostOrderId) -> &mut NodeInfo { + let size = self.num_values(); + self.nodes.ensure_contains_elem(id, || NodeInfo::new(size)); + &mut self.nodes[id] + } + + fn add_control_edge(&mut self, from: PostOrderId, to: PostOrderId) { + trace!("adding control edge from {:?} to {:?}", from, to); + self.node_mut(from).successors.push(to); + } +} + +#[derive(Debug)] +struct NodeInfo { + /// IDs of nodes that can follow this one in the control flow + /// + /// If the vec is empty, then control proceeds to the next node. + successors: Vec, + + /// List of hir_ids that are dropped by this node. + drops: Vec, + + /// List of hir_ids that are reinitialized by this node. + reinits: Vec, + + /// Set of values that are definitely dropped at this point. + drop_state: BitSet, +} + +impl NodeInfo { + fn new(num_values: usize) -> Self { + Self { + successors: vec![], + drops: vec![], + reinits: vec![], + drop_state: BitSet::new_filled(num_values), + } + } +} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs new file mode 100644 index 0000000000..cfed784ea7 --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -0,0 +1,558 @@ +use super::{ + for_each_consumable, record_consumed_borrow::ConsumedAndBorrowedPlaces, DropRangesBuilder, + NodeInfo, PostOrderId, TrackedValue, TrackedValueIndex, +}; +use hir::{ + intravisit::{self, Visitor}, + Body, Expr, ExprKind, Guard, HirId, LoopIdError, +}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_index::vec::IndexVec; +use rustc_middle::{ + hir::map::Map, + ty::{TyCtxt, TypeckResults}, +}; +use std::mem::swap; + +/// Traverses the body to find the control flow graph and locations for the +/// relevant places are dropped or reinitialized. +/// +/// The resulting structure still needs to be iterated to a fixed point, which +/// can be done with propagate_to_fixpoint in cfg_propagate. +pub(super) fn build_control_flow_graph<'tcx>( + hir: Map<'tcx>, + tcx: TyCtxt<'tcx>, + typeck_results: &TypeckResults<'tcx>, + consumed_borrowed_places: ConsumedAndBorrowedPlaces, + body: &'tcx Body<'tcx>, + num_exprs: usize, +) -> DropRangesBuilder { + let mut drop_range_visitor = + DropRangeVisitor::new(hir, tcx, typeck_results, consumed_borrowed_places, num_exprs); + intravisit::walk_body(&mut drop_range_visitor, body); + + drop_range_visitor.drop_ranges.process_deferred_edges(); + + drop_range_visitor.drop_ranges +} + +/// This struct is used to gather the information for `DropRanges` to determine the regions of the +/// HIR tree for which a value is dropped. +/// +/// We are interested in points where a variables is dropped or initialized, and the control flow +/// of the code. We identify locations in code by their post-order traversal index, so it is +/// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`. +/// +/// We make several simplifying assumptions, with the goal of being more conservative than +/// necessary rather than less conservative (since being less conservative is unsound, but more +/// conservative is still safe). These assumptions are: +/// +/// 1. Moving a variable `a` counts as a move of the whole variable. +/// 2. Moving a partial path like `a.b.c` is ignored. +/// 3. Reinitializing through a field (e.g. `a.b.c = 5`) counds as a reinitialization of all of +/// `a`. +/// +/// Some examples: +/// +/// Rule 1: +/// ```rust +/// let mut a = (vec![0], vec![0]); +/// drop(a); +/// // `a` is not considered initialized. +/// ``` +/// +/// Rule 2: +/// ```rust +/// let mut a = (vec![0], vec![0]); +/// drop(a.0); +/// drop(a.1); +/// // `a` is still considered initialized. +/// ``` +/// +/// Rule 3: +/// ```rust +/// let mut a = (vec![0], vec![0]); +/// drop(a); +/// a.1 = vec![1]; +/// // all of `a` is considered initialized +/// ``` + +struct DropRangeVisitor<'a, 'tcx> { + hir: Map<'tcx>, + places: ConsumedAndBorrowedPlaces, + drop_ranges: DropRangesBuilder, + expr_index: PostOrderId, + tcx: TyCtxt<'tcx>, + typeck_results: &'a TypeckResults<'tcx>, + label_stack: Vec<(Option, PostOrderId)>, +} + +impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { + fn new( + hir: Map<'tcx>, + tcx: TyCtxt<'tcx>, + typeck_results: &'a TypeckResults<'tcx>, + places: ConsumedAndBorrowedPlaces, + num_exprs: usize, + ) -> Self { + debug!("consumed_places: {:?}", places.consumed); + let drop_ranges = DropRangesBuilder::new( + places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()), + hir, + num_exprs, + ); + Self { + hir, + places, + drop_ranges, + expr_index: PostOrderId::from_u32(0), + typeck_results, + tcx, + label_stack: vec![], + } + } + + fn record_drop(&mut self, value: TrackedValue) { + if self.places.borrowed.contains(&value) { + debug!("not marking {:?} as dropped because it is borrowed at some point", value); + } else { + debug!("marking {:?} as dropped at {:?}", value, self.expr_index); + let count = self.expr_index; + self.drop_ranges.drop_at(value, count); + } + } + + /// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all + /// expressions. This method consumes a little deeper into the expression when needed. + fn consume_expr(&mut self, expr: &hir::Expr<'_>) { + debug!("consuming expr {:?}, count={:?}", expr.hir_id, self.expr_index); + let places = self + .places + .consumed + .get(&expr.hir_id) + .map_or(vec![], |places| places.iter().cloned().collect()); + for place in places { + for_each_consumable(self.hir, place, |value| self.record_drop(value)); + } + } + + /// Marks an expression as being reinitialized. + /// + /// Note that we always approximated on the side of things being more + /// initialized than they actually are, as opposed to less. In cases such + /// as `x.y = ...`, we would consider all of `x` as being initialized + /// instead of just the `y` field. + /// + /// This is because it is always safe to consider something initialized + /// even when it is not, but the other way around will cause problems. + /// + /// In the future, we will hopefully tighten up these rules to be more + /// precise. + fn reinit_expr(&mut self, expr: &hir::Expr<'_>) { + // Walk the expression to find the base. For example, in an expression + // like `*a[i].x`, we want to find the `a` and mark that as + // reinitialized. + match expr.kind { + ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) => { + // This is the base case, where we have found an actual named variable. + + let location = self.expr_index; + debug!("reinitializing {:?} at {:?}", hir_id, location); + self.drop_ranges.reinit_at(TrackedValue::Variable(*hir_id), location); + } + + ExprKind::Field(base, _) => self.reinit_expr(base), + + // Most expressions do not refer to something where we need to track + // reinitializations. + // + // Some of these may be interesting in the future + ExprKind::Path(..) + | ExprKind::Box(..) + | ExprKind::ConstBlock(..) + | ExprKind::Array(..) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Tup(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Lit(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::DropTemps(..) + | ExprKind::Let(..) + | ExprKind::If(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Index(..) + | ExprKind::AddrOf(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::InlineAsm(..) + | ExprKind::Struct(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) + | ExprKind::Err => (), + } + } + + /// For an expression with an uninhabited return type (e.g. a function that returns !), + /// this adds a self edge to to the CFG to model the fact that the function does not + /// return. + fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) { + let ty = self.typeck_results.expr_ty(expr); + let ty = self.tcx.erase_regions(ty); + let m = self.tcx.parent_module(expr.hir_id).to_def_id(); + let param_env = self.tcx.param_env(m.expect_local()); + if self.tcx.is_ty_uninhabited_from(m, ty, param_env) { + // This function will not return. We model this fact as an infinite loop. + self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1); + } + } + + /// Map a Destination to an equivalent expression node + /// + /// The destination field of a Break or Continue expression can target either an + /// expression or a block. The drop range analysis, however, only deals in + /// expression nodes, so blocks that might be the destination of a Break or Continue + /// will not have a PostOrderId. + /// + /// If the destination is an expression, this function will simply return that expression's + /// hir_id. If the destination is a block, this function will return the hir_id of last + /// expression in the block. + fn find_target_expression_from_destination( + &self, + destination: hir::Destination, + ) -> Result { + destination.target_id.map(|target| { + let node = self.hir.get(target); + match node { + hir::Node::Expr(_) => target, + hir::Node::Block(b) => find_last_block_expression(b), + hir::Node::Param(..) + | hir::Node::Item(..) + | hir::Node::ForeignItem(..) + | hir::Node::TraitItem(..) + | hir::Node::ImplItem(..) + | hir::Node::Variant(..) + | hir::Node::Field(..) + | hir::Node::AnonConst(..) + | hir::Node::Stmt(..) + | hir::Node::PathSegment(..) + | hir::Node::Ty(..) + | hir::Node::TraitRef(..) + | hir::Node::Binding(..) + | hir::Node::Pat(..) + | hir::Node::Arm(..) + | hir::Node::Local(..) + | hir::Node::Ctor(..) + | hir::Node::Lifetime(..) + | hir::Node::GenericParam(..) + | hir::Node::Visibility(..) + | hir::Node::Crate(..) + | hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node), + } + }) + } +} + +fn find_last_block_expression(block: &hir::Block<'_>) -> HirId { + block.expr.map_or_else( + // If there is no tail expression, there will be at least one statement in the + // block because the block contains a break or continue statement. + || block.stmts.last().unwrap().hir_id, + |expr| expr.hir_id, + ) +} + +impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + let mut reinit = None; + match expr.kind { + ExprKind::Assign(lhs, rhs, _) => { + self.visit_expr(lhs); + self.visit_expr(rhs); + + reinit = Some(lhs); + } + + ExprKind::If(test, if_true, if_false) => { + self.visit_expr(test); + + let fork = self.expr_index; + + self.drop_ranges.add_control_edge(fork, self.expr_index + 1); + self.visit_expr(if_true); + let true_end = self.expr_index; + + self.drop_ranges.add_control_edge(fork, self.expr_index + 1); + if let Some(if_false) = if_false { + self.visit_expr(if_false); + } + + self.drop_ranges.add_control_edge(true_end, self.expr_index + 1); + } + ExprKind::Match(scrutinee, arms, ..) => { + // We walk through the match expression almost like a chain of if expressions. + // Here's a diagram to follow along with: + // + // ┌─┐ + // match │A│ { + // ┌───┴─┘ + // │ + // ┌▼┌───►┌─┐ ┌─┐ + // │B│ if │C│ =>│D│, + // └─┘ ├─┴──►└─┴──────┐ + // ┌──┘ │ + // ┌──┘ │ + // │ │ + // ┌▼┌───►┌─┐ ┌─┐ │ + // │E│ if │F│ =>│G│, │ + // └─┘ ├─┴──►└─┴┐ │ + // │ │ │ + // } ▼ ▼ │ + // ┌─┐◄───────────────────┘ + // │H│ + // └─┘ + // + // The order we want is that the scrutinee (A) flows into the first pattern (B), + // which flows into the guard (C). Then the guard either flows into the arm body + // (D) or into the start of the next arm (E). Finally, the body flows to the end + // of the match block (H). + // + // The subsequent arms follow the same ordering. First we go to the pattern, then + // the guard (if present, otherwise it flows straight into the body), then into + // the body and then to the end of the match expression. + // + // The comments below show which edge is being added. + self.visit_expr(scrutinee); + + let (guard_exit, arm_end_ids) = arms.iter().fold( + (self.expr_index, vec![]), + |(incoming_edge, mut arm_end_ids), hir::Arm { pat, body, guard, .. }| { + // A -> B, or C -> E + self.drop_ranges.add_control_edge(incoming_edge, self.expr_index + 1); + self.visit_pat(pat); + // B -> C and E -> F are added implicitly due to the traversal order. + match guard { + Some(Guard::If(expr)) => self.visit_expr(expr), + Some(Guard::IfLet(pat, expr)) => { + self.visit_pat(pat); + self.visit_expr(expr); + } + None => (), + } + // Likewise, C -> D and F -> G are added implicitly. + + // Save C, F, so we can add the other outgoing edge. + let to_next_arm = self.expr_index; + + // The default edge does not get added since we also have an explicit edge, + // so we also need to add an edge to the next node as well. + // + // This adds C -> D, F -> G + self.drop_ranges.add_control_edge(self.expr_index, self.expr_index + 1); + self.visit_expr(body); + + // Save the end of the body so we can add the exit edge once we know where + // the exit is. + arm_end_ids.push(self.expr_index); + + // Pass C to the next iteration, as well as vec![D] + // + // On the last round through, we pass F and vec![D, G] so that we can + // add all the exit edges. + (to_next_arm, arm_end_ids) + }, + ); + // F -> H + self.drop_ranges.add_control_edge(guard_exit, self.expr_index + 1); + + arm_end_ids.into_iter().for_each(|arm_end| { + // D -> H, G -> H + self.drop_ranges.add_control_edge(arm_end, self.expr_index + 1) + }); + } + + ExprKind::Loop(body, label, ..) => { + let loop_begin = self.expr_index + 1; + self.label_stack.push((label, loop_begin)); + if body.stmts.is_empty() && body.expr.is_none() { + // For empty loops we won't have updated self.expr_index after visiting the + // body, meaning we'd get an edge from expr_index to expr_index + 1, but + // instead we want an edge from expr_index + 1 to expr_index + 1. + self.drop_ranges.add_control_edge(loop_begin, loop_begin); + } else { + self.visit_block(body); + self.drop_ranges.add_control_edge(self.expr_index, loop_begin); + } + self.label_stack.pop(); + } + // Find the loop entry by searching through the label stack for either the last entry + // (if label is none), or the first entry where the label matches this one. The Loop + // case maintains this stack mapping labels to the PostOrderId for the loop entry. + ExprKind::Continue(hir::Destination { label, .. }, ..) => self + .label_stack + .iter() + .rev() + .find(|(loop_label, _)| label.is_none() || *loop_label == label) + .map_or((), |(_, target)| { + self.drop_ranges.add_control_edge(self.expr_index, *target) + }), + + ExprKind::Break(destination, ..) => { + // destination either points to an expression or to a block. We use + // find_target_expression_from_destination to use the last expression of the block + // if destination points to a block. + // + // We add an edge to the hir_id of the expression/block we are breaking out of, and + // then in process_deferred_edges we will map this hir_id to its PostOrderId, which + // will refer to the end of the block due to the post order traversal. + self.find_target_expression_from_destination(destination).map_or((), |target| { + self.drop_ranges.add_control_edge_hir_id(self.expr_index, target) + }) + } + + ExprKind::Call(f, args) => { + self.visit_expr(f); + for arg in args { + self.visit_expr(arg); + } + + self.handle_uninhabited_return(expr); + } + ExprKind::MethodCall(_, exprs, _) => { + for expr in exprs { + self.visit_expr(expr); + } + + self.handle_uninhabited_return(expr); + } + + ExprKind::AddrOf(..) + | ExprKind::Array(..) + | ExprKind::AssignOp(..) + | ExprKind::Binary(..) + | ExprKind::Block(..) + | ExprKind::Box(..) + | ExprKind::Cast(..) + | ExprKind::Closure(..) + | ExprKind::ConstBlock(..) + | ExprKind::DropTemps(..) + | ExprKind::Err + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::InlineAsm(..) + | ExprKind::Let(..) + | ExprKind::Lit(..) + | ExprKind::Path(..) + | ExprKind::Repeat(..) + | ExprKind::Ret(..) + | ExprKind::Struct(..) + | ExprKind::Tup(..) + | ExprKind::Type(..) + | ExprKind::Unary(..) + | ExprKind::Yield(..) => intravisit::walk_expr(self, expr), + } + + self.expr_index = self.expr_index + 1; + self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_index); + self.consume_expr(expr); + if let Some(expr) = reinit { + self.reinit_expr(expr); + } + } + + fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + intravisit::walk_pat(self, pat); + + // Increment expr_count here to match what InteriorVisitor expects. + self.expr_index = self.expr_index + 1; + } +} + +impl DropRangesBuilder { + fn new( + tracked_values: impl Iterator, + hir: Map<'_>, + num_exprs: usize, + ) -> Self { + let mut tracked_value_map = FxHashMap::<_, TrackedValueIndex>::default(); + let mut next = <_>::from(0u32); + for value in tracked_values { + for_each_consumable(hir, value, |value| { + if !tracked_value_map.contains_key(&value) { + tracked_value_map.insert(value, next); + next = next + 1; + } + }); + } + debug!("hir_id_map: {:?}", tracked_value_map); + let num_values = tracked_value_map.len(); + Self { + tracked_value_map, + nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1), + deferred_edges: <_>::default(), + post_order_map: <_>::default(), + } + } + + fn tracked_value_index(&self, tracked_value: TrackedValue) -> TrackedValueIndex { + *self.tracked_value_map.get(&tracked_value).unwrap() + } + + /// Adds an entry in the mapping from HirIds to PostOrderIds + /// + /// Needed so that `add_control_edge_hir_id` can work. + fn add_node_mapping(&mut self, node_hir_id: HirId, post_order_id: PostOrderId) { + self.post_order_map.insert(node_hir_id, post_order_id); + } + + /// Like add_control_edge, but uses a hir_id as the target. + /// + /// This can be used for branches where we do not know the PostOrderId of the target yet, + /// such as when handling `break` or `continue`. + fn add_control_edge_hir_id(&mut self, from: PostOrderId, to: HirId) { + self.deferred_edges.push((from, to)); + } + + fn drop_at(&mut self, value: TrackedValue, location: PostOrderId) { + let value = self.tracked_value_index(value); + self.node_mut(location).drops.push(value); + } + + fn reinit_at(&mut self, value: TrackedValue, location: PostOrderId) { + let value = match self.tracked_value_map.get(&value) { + Some(value) => *value, + // If there's no value, this is never consumed and therefore is never dropped. We can + // ignore this. + None => return, + }; + self.node_mut(location).reinits.push(value); + } + + /// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them. + /// + /// Should be called after visiting the HIR but before solving the control flow, otherwise some + /// edges will be missed. + fn process_deferred_edges(&mut self) { + trace!("processing deferred edges. post_order_map={:#?}", self.post_order_map); + let mut edges = vec![]; + swap(&mut edges, &mut self.deferred_edges); + edges.into_iter().for_each(|(from, to)| { + trace!("Adding deferred edge from {:?} to {:?}", from, to); + let to = *self.post_order_map.get(&to).expect("Expression ID not found"); + trace!("target edge PostOrderId={:?}", to); + self.add_control_edge(from, to) + }); + } +} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs new file mode 100644 index 0000000000..139d17d2e1 --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs @@ -0,0 +1,92 @@ +use super::{DropRangesBuilder, PostOrderId}; +use rustc_index::{bit_set::BitSet, vec::IndexVec}; +use std::collections::BTreeMap; + +impl DropRangesBuilder { + pub fn propagate_to_fixpoint(&mut self) { + trace!("before fixpoint: {:#?}", self); + let preds = self.compute_predecessors(); + + trace!("predecessors: {:#?}", preds.iter_enumerated().collect::>()); + + let mut new_state = BitSet::new_empty(self.num_values()); + let mut changed_nodes = BitSet::new_empty(self.nodes.len()); + let mut unchanged_mask = BitSet::new_filled(self.nodes.len()); + changed_nodes.insert(0u32.into()); + + let mut propagate = || { + let mut changed = false; + unchanged_mask.insert_all(); + for id in self.nodes.indices() { + trace!("processing {:?}, changed_nodes: {:?}", id, changed_nodes); + // Check if any predecessor has changed, and if not then short-circuit. + // + // We handle the start node specially, since it doesn't have any predecessors, + // but we need to start somewhere. + if match id.index() { + 0 => !changed_nodes.contains(id), + _ => !preds[id].iter().any(|pred| changed_nodes.contains(*pred)), + } { + trace!("short-circuiting because none of {:?} have changed", preds[id]); + unchanged_mask.remove(id); + continue; + } + + if id.index() == 0 { + new_state.clear(); + } else { + // If we are not the start node and we have no predecessors, treat + // everything as dropped because there's no way to get here anyway. + new_state.insert_all(); + }; + + for pred in &preds[id] { + new_state.intersect(&self.nodes[*pred].drop_state); + } + + for drop in &self.nodes[id].drops { + new_state.insert(*drop); + } + + for reinit in &self.nodes[id].reinits { + new_state.remove(*reinit); + } + + if self.nodes[id].drop_state.intersect(&new_state) { + changed_nodes.insert(id); + changed = true; + } else { + unchanged_mask.remove(id); + } + } + + changed_nodes.intersect(&unchanged_mask); + changed + }; + + while propagate() { + trace!("drop_state changed, re-running propagation"); + } + + trace!("after fixpoint: {:#?}", self); + } + + fn compute_predecessors(&self) -> IndexVec> { + let mut preds = IndexVec::from_fn_n(|_| vec![], self.nodes.len()); + for (id, node) in self.nodes.iter_enumerated() { + // If the node has no explicit successors, we assume that control + // will from this node into the next one. + // + // If there are successors listed, then we assume that all + // possible successors are given and we do not include the default. + if node.successors.len() == 0 && id.index() != self.nodes.len() - 1 { + preds[id + 1].push(id); + } else { + for succ in &node.successors { + preds[*succ].push(id); + } + } + } + preds + } +} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs new file mode 100644 index 0000000000..20aad7aedf --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs @@ -0,0 +1,77 @@ +//! Implementation of GraphWalk for DropRanges so we can visualize the control +//! flow graph when needed for debugging. + +use rustc_graphviz as dot; + +use super::{DropRangesBuilder, PostOrderId}; + +/// Writes the CFG for DropRangesBuilder to a .dot file for visualization. +/// +/// It is not normally called, but is kept around to easily add debugging +/// code when needed. +#[allow(dead_code)] +pub(super) fn write_graph_to_file(drop_ranges: &DropRangesBuilder, filename: &str) { + dot::render(drop_ranges, &mut std::fs::File::create(filename).unwrap()).unwrap(); +} + +impl<'a> dot::GraphWalk<'a> for DropRangesBuilder { + type Node = PostOrderId; + + type Edge = (PostOrderId, PostOrderId); + + fn nodes(&'a self) -> dot::Nodes<'a, Self::Node> { + self.nodes.iter_enumerated().map(|(i, _)| i).collect() + } + + fn edges(&'a self) -> dot::Edges<'a, Self::Edge> { + self.nodes + .iter_enumerated() + .flat_map(|(i, node)| { + if node.successors.len() == 0 { + vec![(i, i + 1)] + } else { + node.successors.iter().map(move |&s| (i, s)).collect() + } + }) + .collect() + } + + fn source(&'a self, edge: &Self::Edge) -> Self::Node { + edge.0 + } + + fn target(&'a self, edge: &Self::Edge) -> Self::Node { + edge.1 + } +} + +impl<'a> dot::Labeller<'a> for DropRangesBuilder { + type Node = PostOrderId; + + type Edge = (PostOrderId, PostOrderId); + + fn graph_id(&'a self) -> dot::Id<'a> { + dot::Id::new("drop_ranges").unwrap() + } + + fn node_id(&'a self, n: &Self::Node) -> dot::Id<'a> { + dot::Id::new(format!("id{}", n.index())).unwrap() + } + + fn node_label(&'a self, n: &Self::Node) -> dot::LabelText<'a> { + dot::LabelText::LabelStr( + format!( + "{:?}, local_id: {}", + n, + self.post_order_map + .iter() + .find(|(_hir_id, &post_order_id)| post_order_id == *n) + .map_or("".into(), |(hir_id, _)| format!( + "{}", + hir_id.local_id.index() + )) + ) + .into(), + ) + } +} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs new file mode 100644 index 0000000000..9a308586af --- /dev/null +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -0,0 +1,118 @@ +use super::TrackedValue; +use crate::{ + check::FnCtxt, + expr_use_visitor::{self, ExprUseVisitor}, +}; +use hir::{def_id::DefId, Body, HirId, HirIdMap}; +use rustc_data_structures::stable_set::FxHashSet; +use rustc_hir as hir; +use rustc_middle::hir::map::Map; + +pub(super) fn find_consumed_and_borrowed<'a, 'tcx>( + fcx: &'a FnCtxt<'a, 'tcx>, + def_id: DefId, + body: &'tcx Body<'tcx>, +) -> ConsumedAndBorrowedPlaces { + let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx.hir()); + expr_use_visitor.consume_body(fcx, def_id, body); + expr_use_visitor.places +} + +pub(super) struct ConsumedAndBorrowedPlaces { + /// Records the variables/expressions that are dropped by a given expression. + /// + /// The key is the hir-id of the expression, and the value is a set or hir-ids for variables + /// or values that are consumed by that expression. + /// + /// Note that this set excludes "partial drops" -- for example, a statement like `drop(x.y)` is + /// not considered a drop of `x`, although it would be a drop of `x.y`. + pub(super) consumed: HirIdMap>, + /// A set of hir-ids of values or variables that are borrowed at some point within the body. + pub(super) borrowed: FxHashSet, +} + +/// Works with ExprUseVisitor to find interesting values for the drop range analysis. +/// +/// Interesting values are those that are either dropped or borrowed. For dropped values, we also +/// record the parent expression, which is the point where the drop actually takes place. +struct ExprUseDelegate<'tcx> { + hir: Map<'tcx>, + places: ConsumedAndBorrowedPlaces, +} + +impl<'tcx> ExprUseDelegate<'tcx> { + fn new(hir: Map<'tcx>) -> Self { + Self { + hir, + places: ConsumedAndBorrowedPlaces { + consumed: <_>::default(), + borrowed: <_>::default(), + }, + } + } + + fn consume_body(&mut self, fcx: &'_ FnCtxt<'_, 'tcx>, def_id: DefId, body: &'tcx Body<'tcx>) { + // Run ExprUseVisitor to find where values are consumed. + ExprUseVisitor::new( + self, + &fcx.infcx, + def_id.expect_local(), + fcx.param_env, + &fcx.typeck_results.borrow(), + ) + .consume_body(body); + } + + fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) { + if !self.places.consumed.contains_key(&consumer) { + self.places.consumed.insert(consumer, <_>::default()); + } + self.places.consumed.get_mut(&consumer).map(|places| places.insert(target)); + } +} + +impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { + fn consume( + &mut self, + place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, + diag_expr_id: HirId, + ) { + let parent = match self.hir.find_parent_node(place_with_id.hir_id) { + Some(parent) => parent, + None => place_with_id.hir_id, + }; + debug!( + "consume {:?}; diag_expr_id={:?}, using parent {:?}", + place_with_id, diag_expr_id, parent + ); + place_with_id + .try_into() + .map_or((), |tracked_value| self.mark_consumed(parent, tracked_value)); + } + + fn borrow( + &mut self, + place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, + _diag_expr_id: HirId, + _bk: rustc_middle::ty::BorrowKind, + ) { + self.places + .borrowed + .insert(TrackedValue::from_place_with_projections_allowed(place_with_id)); + } + + fn mutate( + &mut self, + _assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>, + _diag_expr_id: HirId, + ) { + } + + fn fake_read( + &mut self, + _place: expr_use_visitor::Place<'tcx>, + _cause: rustc_middle::mir::FakeReadCause, + _diag_expr_id: HirId, + ) { + } +} diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 6314f2aba4..74f6f50d41 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -297,6 +297,11 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::const_allocate => { (0, vec![tcx.types.usize, tcx.types.usize], tcx.mk_mut_ptr(tcx.types.u8)) } + sym::const_deallocate => ( + 0, + vec![tcx.mk_mut_ptr(tcx.types.u8), tcx.types.usize, tcx.types.usize], + tcx.mk_unit(), + ), sym::ptr_offset_from => { (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize) @@ -453,7 +458,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()), sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)), sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)), - sym::simd_cast => (2, vec![param(0)], param(1)), + sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)), sym::simd_bitmask => (2, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, vec![param(0), param(1), param(1)], param(1)) diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index dabfe92190..fdc3ba17e3 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -149,7 +149,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // time writing the results into the various typeck results. let mut autoderef = self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); - let (_, n) = match autoderef.nth(pick.autoderefs) { + let (ty, n) = match autoderef.nth(pick.autoderefs) { Some(n) => n, None => { return self.tcx.ty_error_with_message( @@ -161,14 +161,15 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { assert_eq!(n, pick.autoderefs); let mut adjustments = self.adjust_steps(&autoderef); + let mut target = self.structurally_resolved_type(autoderef.span(), ty); - let mut target = - self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - - match &pick.autoref_or_ptr_adjustment { + match pick.autoref_or_ptr_adjustment { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { let region = self.next_region_var(infer::Autoref(self.span)); - target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: *mutbl, ty: target }); + // Type we're wrapping in a reference, used later for unsizing + let base_ty = target; + + target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target }); let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, hir::Mutability::Mut => AutoBorrowMutability::Mut { @@ -182,18 +183,26 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { target, }); - if let Some(unsize_target) = unsize { + if unsize { + let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() { + self.tcx.mk_slice(*elem_ty) + } else { + bug!( + "AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}", + base_ty + ) + }; target = self .tcx - .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target }); + .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty }); adjustments .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }); } } Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { target = match target.kind() { - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { - assert_eq!(*mutbl, hir::Mutability::Mut); + &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + assert_eq!(mutbl, hir::Mutability::Mut); self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) } other => panic!("Cannot adjust receiver type {:?} to const ptr", other), diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index f7f4c52c2a..e5ef52e032 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -227,7 +227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Ref(region, t_type, mutability) = self_ty.kind() { let trait_type = self .tcx - .mk_ref(region, ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() }); + .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }); // We probe again to see if there might be a borrow mutability discrepancy. match self.lookup_probe( span, @@ -359,6 +359,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (obligation, substs) = self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); + debug!(?obligation); + // Now we want to know if this can be matched if !self.predicate_may_hold(&obligation) { debug!("--> Cannot match obligation"); @@ -369,7 +371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Trait must have a method named `m_name` and it should not have // type parameters or early-bound regions. let tcx = self.tcx; - let method_item = match self.associated_item(trait_def_id, m_name, Namespace::ValueNS) { + let method_item = match self.associated_value(trait_def_id, m_name) { Some(method_item) => method_item, None => { tcx.sess.delay_span_bug( @@ -482,7 +484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let variant_def = adt_def .variants .iter() - .find(|vd| tcx.hygienic_eq(method_name, vd.ident, adt_def.did)); + .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did)); if let Some(variant_def) = variant_def { // Braced variants generate unusable names in value namespace (reserved for // possible future use), so variants resolved as associated items may refer to @@ -538,15 +540,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Finds item with name `item_name` defined in impl/trait `def_id` /// and return it, or `None`, if no such item was defined there. - pub fn associated_item( - &self, - def_id: DefId, - item_name: Ident, - ns: Namespace, - ) -> Option { + pub fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option { self.tcx .associated_items(def_id) - .find_by_name_and_namespace(self.tcx, item_name, ns, def_id) + .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id) .copied() } } diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 5615a08369..c429e0f165 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -9,7 +9,6 @@ use crate::hir::def::DefKind; use crate::hir::def_id::DefId; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Namespace; @@ -59,7 +58,7 @@ struct ProbeContext<'a, 'tcx> { /// This is the OriginalQueryValues for the steps queries /// that are answered in steps. orig_steps_var_values: OriginalQueryValues<'tcx>, - steps: Lrc>>, + steps: &'tcx [CandidateStep<'tcx>], inherent_candidates: Vec>, extension_candidates: Vec>, @@ -167,26 +166,26 @@ enum ProbeResult { /// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do /// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with /// `mut`), or it has type `*mut T` and we convert it to `*const T`. -#[derive(Debug, PartialEq, Clone)] -pub enum AutorefOrPtrAdjustment<'tcx> { +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum AutorefOrPtrAdjustment { /// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it. /// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing. Autoref { mutbl: hir::Mutability, - /// Indicates that the source expression should be "unsized" to a target type. This should - /// probably eventually go away in favor of just coercing method receivers. - unsize: Option>, + /// Indicates that the source expression should be "unsized" to a target type. + /// This is special-cased for just arrays unsizing to slices. + unsize: bool, }, /// Receiver has type `*mut T`, convert to `*const T` ToConstPtr, } -impl<'tcx> AutorefOrPtrAdjustment<'tcx> { - fn get_unsize(&self) -> Option> { +impl AutorefOrPtrAdjustment { + fn get_unsize(&self) -> bool { match self { AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize, - AutorefOrPtrAdjustment::ToConstPtr => None, + AutorefOrPtrAdjustment::ToConstPtr => false, } } } @@ -204,7 +203,7 @@ pub struct Pick<'tcx> { /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is /// `*mut T`, convert it to `*const T`. - pub autoref_or_ptr_adjustment: Option>, + pub autoref_or_ptr_adjustment: Option, pub self_ty: Ty<'tcx>, } @@ -364,7 +363,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env_and_self_ty, self_ty ); MethodAutoderefStepsResult { - steps: Lrc::new(vec![CandidateStep { + steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { self_ty: self.make_query_response_ignoring_pending_obligations( canonical_inference_vars, self_ty, @@ -516,7 +515,7 @@ fn method_autoderef_steps<'tcx>( steps.push(CandidateStep { self_ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, - infcx.tcx.mk_slice(elem_ty), + infcx.tcx.mk_slice(*elem_ty), ), autoderefs: dereferences, // this could be from an unsafe deref if we had @@ -533,8 +532,8 @@ fn method_autoderef_steps<'tcx>( debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); MethodAutoderefStepsResult { - steps: Lrc::new(steps), - opt_bad_ty: opt_bad_ty.map(Lrc::new), + steps: tcx.arena.alloc_from_iter(steps), + opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), reached_recursion_limit: autoderef.reached_recursion_limit(), } }) @@ -548,7 +547,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { method_name: Option, return_type: Option>, orig_steps_var_values: OriginalQueryValues<'tcx>, - steps: Lrc>>, + steps: &'tcx [CandidateStep<'tcx>], is_suggestion: IsSuggestion, scope_expr_id: hir::HirId, ) -> ProbeContext<'a, 'tcx> { @@ -605,8 +604,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } fn assemble_inherent_candidates(&mut self) { - let steps = Lrc::clone(&self.steps); - for step in steps.iter() { + for step in self.steps.iter() { self.assemble_probe(&step.self_ty); } } @@ -1033,7 +1031,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { true } }) - .map(|candidate| candidate.item.ident) + .map(|candidate| candidate.item.ident(self.tcx)) .filter(|&name| set.insert(name)) .collect(); @@ -1202,7 +1200,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pick.autoderefs += 1; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { mutbl, - unsize: pick.autoref_or_ptr_adjustment.and_then(|a| a.get_unsize()), + unsize: pick.autoref_or_ptr_adjustment.map_or(false, |a| a.get_unsize()), }) } @@ -1227,10 +1225,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.pick_method(autoref_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; - pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { - mutbl, - unsize: step.unsize.then_some(self_ty), - }); + pick.autoref_or_ptr_adjustment = + Some(AutorefOrPtrAdjustment::Autoref { mutbl, unsize: step.unsize }); pick }) }) @@ -1251,7 +1247,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } let ty = match self_ty.kind() { - ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) => ty, + &ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) => ty, _ => return None, }; @@ -1440,7 +1436,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { "<{} as {}>::{}", stable_pick.self_ty, self.tcx.def_path_str(def_id), - stable_pick.item.ident + stable_pick.item.name ), Applicability::MachineApplicable, ); @@ -1750,14 +1746,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let best_name = { let names = applicable_close_candidates .iter() - .map(|cand| cand.ident.name) + .map(|cand| cand.name) .collect::>(); find_best_match_for_name(&names, self.method_name.unwrap().name, None) } .unwrap(); - Ok(applicable_close_candidates - .into_iter() - .find(|method| method.ident.name == best_name)) + Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name)) } }) } @@ -1908,14 +1902,19 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .associated_items(def_id) .in_definition_order() .filter(|x| { - let dist = lev_distance(name.as_str(), x.ident.as_str()); - x.kind.namespace() == Namespace::ValueNS && dist > 0 && dist <= max_dist + if x.kind.namespace() != Namespace::ValueNS { + return false; + } + match lev_distance(name.as_str(), x.name.as_str(), max_dist) { + Some(d) => d > 0, + None => false, + } }) .copied() .collect() } else { self.fcx - .associated_item(def_id, name, Namespace::ValueNS) + .associated_value(def_id, name) .map_or_else(SmallVec::new, |x| SmallVec::from_buf([x])) } } else { diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 7f9c75c7fe..a523ba286e 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -5,17 +5,18 @@ use crate::check::FnCtxt; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def::Namespace; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::fast_reject::{simplify_type, SimplifyParams, StripReferences}; +use rustc_middle::traits::util::supertraits; +use rustc_middle::ty::fast_reject::{simplify_type, SimplifyParams}; use rustc_middle::ty::print::with_crate_prefix; +use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_span::lev_distance; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{source_map, FileName, MultiSpan, Span, Symbol}; +use rustc_span::{source_map, FileName, MultiSpan, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, @@ -41,7 +42,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(..) => return false, }; + // This conditional prevents us from asking to call errors and unresolved types. + // It might seem that we can use `predicate_must_hold_modulo_regions`, + // but since a Dummy binder is used to fill in the FnOnce trait's arguments, + // type resolution always gives a "maybe" here. + if self.autoderef(span, ty).any(|(ty, _)| { + info!("check deref {:?} error", ty); + matches!(ty.kind(), ty::Error(_) | ty::Infer(_)) + }) { + return false; + } + self.autoderef(span, ty).any(|(ty, _)| { + info!("check deref {:?} impl FnOnce", ty); self.probe(|_| { let fn_once_substs = tcx.mk_substs_trait( ty, @@ -99,16 +112,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { CandidateSource::ImplSource(impl_did) => { // Provide the best span we can. Use the item, if local to crate, else // the impl, if local to crate (item may be defaulted), else nothing. - let item = match self - .associated_item(impl_did, item_name, Namespace::ValueNS) - .or_else(|| { - let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?; - self.associated_item( - impl_trait_ref.def_id, - item_name, - Namespace::ValueNS, - ) - }) { + let item = match self.associated_value(impl_did, item_name).or_else(|| { + let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?; + self.associated_value(impl_trait_ref.def_id, item_name) + }) { Some(item) => item, None => continue, }; @@ -187,11 +194,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } CandidateSource::TraitSource(trait_did) => { - let item = - match self.associated_item(trait_did, item_name, Namespace::ValueNS) { - Some(item) => item, - None => continue, - }; + let item = match self.associated_value(trait_did, item_name) { + Some(item) => item, + None => continue, + }; let item_span = self .tcx .sess @@ -271,16 +277,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Suggest clamping down the type if the method that is being attempted to // be used exists at all, and the type is an ambiguous numeric type // ({integer}/{float}). - let mut candidates = all_traits(self.tcx).into_iter().filter_map(|info| { - self.associated_item(info.def_id, item_name, Namespace::ValueNS) - }); + let mut candidates = all_traits(self.tcx) + .into_iter() + .filter_map(|info| self.associated_value(info.def_id, item_name)); // There are methods that are defined on the primitive types and won't be // found when exploring `all_traits`, but we also need them to be acurate on // our suggestions (#47759). let fund_assoc = |opt_def_id: Option| { - opt_def_id - .and_then(|id| self.associated_item(id, item_name, Namespace::ValueNS)) - .is_some() + opt_def_id.and_then(|id| self.associated_value(id, item_name)).is_some() }; let lang_items = tcx.lang_items(); let found_candidate = candidates.next().is_some() @@ -398,11 +402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .inherent_impls(adt_deref.did) .iter() .filter_map(|def_id| { - self.associated_item( - *def_id, - item_name, - Namespace::ValueNS, - ) + self.associated_value(*def_id, item_name) }) .count() >= 1 @@ -515,9 +515,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .copied() .filter(|def_id| { - if let Some(assoc) = - self.associated_item(*def_id, item_name, Namespace::ValueNS) - { + if let Some(assoc) = self.associated_value(*def_id, item_name) { // Check for both mode is the same so we avoid suggesting // incorrect associated item. match (mode, assoc.fn_has_self_parameter, source) { @@ -719,7 +717,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut bound_spans = vec![]; let mut collect_type_param_suggestions = - |self_ty: Ty<'tcx>, parent_pred: &ty::Predicate<'tcx>, obligation: &str| { + |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::PredicateKind::Trait(p)) = (self_ty.kind(), parent_pred.kind().skip_binder()) @@ -805,10 +803,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_def_id: projection_ty.item_def_id, }; - let ty = pred.skip_binder().ty; + let term = pred.skip_binder().term; - let obligation = format!("{} = {}", projection_ty, ty); - let quiet = format!("{} = {}", quiet_projection_ty, ty); + let obligation = format!("{} = {}", projection_ty, term); + let quiet = format!("{} = {}", quiet_projection_ty, term); bound_span_label(projection_ty.self_ty(), &obligation, &quiet); Some((obligation, projection_ty.self_ty())) @@ -839,9 +837,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }) { - let parent_trait_ref = data.parent_trait_ref; + let parent_trait_ref = data.parent_trait_pred; let parent_def_id = parent_trait_ref.def_id(); - let path = parent_trait_ref.print_only_trait_path(); + let path = parent_trait_ref.print_modifiers_and_trait_path(); let tr_self_ty = parent_trait_ref.skip_binder().self_ty(); let mut candidates = vec![]; self.tcx.for_each_relevant_impl( @@ -908,7 +906,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter(|(pred, _, _parent_pred)| !skip_list.contains(&pred)) .filter_map(|(pred, parent_pred, _cause)| { format_pred(*pred).map(|(p, self_ty)| { - collect_type_param_suggestions(self_ty, pred, &p); + collect_type_param_suggestions(self_ty, *pred, &p); match parent_pred { None => format!("`{}`", &p), Some(parent_pred) => match format_pred(*parent_pred) { @@ -916,7 +914,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((parent_p, _)) => { collect_type_param_suggestions( self_ty, - parent_pred, + *parent_pred, &p, ); format!("`{}`\nwhich is required by `{}`", p, parent_p) @@ -997,7 +995,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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::>(), + &adt_def.variants.iter().map(|s| s.name).collect::>(), item_name.name, None, ) { @@ -1041,7 +1039,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_kind.article(), def_kind.descr(lev_candidate.def_id), ), - lev_candidate.ident.to_string(), + lev_candidate.name.to_string(), Applicability::MaybeIncorrect, ); } @@ -1102,8 +1100,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { if needs_mut { let trait_type = self.tcx.mk_ref( - region, - ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() }, + *region, + ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, ); err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); } @@ -1212,9 +1210,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(adt) if adt.did.is_local() => adt, _ => continue, }; - let can_derive = match self.tcx.get_diagnostic_name(trait_pred.def_id()) { - Some(sym::Default) => !adt.is_enum(), - Some( + if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) { + let can_derive = match diagnostic_name { + sym::Default => !adt.is_enum(), sym::Eq | sym::PartialEq | sym::Ord @@ -1222,16 +1220,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | sym::Clone | sym::Copy | sym::Hash - | sym::Debug, - ) => true, - _ => false, - }; - if can_derive { - derives.push(( - format!("{}", trait_pred.self_ty()), - self.tcx.def_span(adt.did), - format!("{}", trait_pred.trait_ref.print_only_trait_name()), - )); + | sym::Debug => true, + _ => false, + }; + if can_derive { + let self_name = trait_pred.self_ty().to_string(); + let self_span = self.tcx.def_span(adt.did); + if let Some(poly_trait_ref) = pred.to_opt_poly_trait_pred() { + for super_trait in supertraits(self.tcx, poly_trait_ref.to_poly_trait_ref()) + { + if let Some(parent_diagnostic_name) = + self.tcx.get_diagnostic_name(super_trait.def_id()) + { + derives.push(( + self_name.clone(), + self_span.clone(), + parent_diagnostic_name.to_string(), + )); + } + } + } + derives.push((self_name, self_span, diagnostic_name.to_string())); + } else { + traits.push(self.tcx.def_span(trait_pred.def_id())); + } } else { traits.push(self.tcx.def_span(trait_pred.def_id())); } @@ -1290,7 +1302,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, ) { let output_ty = match self.infcx.get_impl_future_output_ty(ty) { - Some(output_ty) => self.resolve_vars_if_possible(output_ty), + Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(), _ => return, }; let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); @@ -1321,7 +1333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if Some(*parent_did) != self.tcx.parent(*trait_did) && self .tcx - .item_children(*parent_did) + .module_children(*parent_did) .iter() .filter(|child| child.res.opt_def_id() == Some(*trait_did)) .all(|child| child.ident.name == kw::Underscore) @@ -1478,13 +1490,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // just this list. for (rcvr_ty, post) in &[ (rcvr_ty, ""), - (self.tcx.mk_mut_ref(&ty::ReErased, rcvr_ty), "&mut "), - (self.tcx.mk_imm_ref(&ty::ReErased, rcvr_ty), "&"), + (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), + (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), ] { if let Ok(pick) = self.lookup_probe( span, item_name, - rcvr_ty, + *rcvr_ty, rcvr, crate::check::method::probe::ProbeScope::AllTraits, ) { @@ -1496,17 +1508,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let skip = skippable.contains(&did); if pick.autoderefs == 0 && !skip { err.span_label( - pick.item.ident.span, + pick.item.ident(self.tcx).span, &format!("the method is available for `{}` here", rcvr_ty), ); } break; } for (rcvr_ty, pre) in &[ - (self.tcx.mk_lang_item(rcvr_ty, LangItem::OwnedBox), "Box::new"), - (self.tcx.mk_lang_item(rcvr_ty, LangItem::Pin), "Pin::new"), - (self.tcx.mk_diagnostic_item(rcvr_ty, sym::Arc), "Arc::new"), - (self.tcx.mk_diagnostic_item(rcvr_ty, sym::Rc), "Rc::new"), + (self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"), + (self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"), + (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"), + (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), ] { if let Some(new_rcvr_t) = *rcvr_ty { if let Ok(pick) = self.lookup_probe( @@ -1524,14 +1536,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not // implement the `AsRef` trait. let skip = skippable.contains(&did) - || (("Pin::new" == *pre) - && (Symbol::intern("as_ref") == item_name.name)); + || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)); // Make sure the method is defined for the *actual* receiver: we don't // want to treat `Box` as a receiver if it only works because of // an autoderef to `&self` if pick.autoderefs == 0 && !skip { err.span_label( - pick.item.ident.span, + pick.item.ident(self.tcx).span, &format!("the method is available for `{}` here", new_rcvr_t), ); err.multipart_suggestion( @@ -1588,7 +1599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }) && (type_is_local || info.def_id.is_local()) && self - .associated_item(info.def_id, item_name, Namespace::ValueNS) + .associated_value(info.def_id, item_name) .filter(|item| { if let ty::AssocKind::Fn = item.kind { let id = item @@ -1680,7 +1691,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let table_owner = table.borrow().hir_owner; let generics = self.tcx.generics_of(table_owner.to_def_id()); let type_param = generics.type_param(param, self.tcx); - let hir = &self.tcx.hir(); + let hir = self.tcx.hir(); if let Some(def_id) = type_param.def_id.as_local() { let id = hir.local_def_id_to_hir_id(def_id); // Get the `hir::Param` to verify whether it already has any bounds. @@ -1765,8 +1776,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME: Even though negative bounds are not implemented, we could maybe handle // cases where a positive bound implies a negative impl. (candidates, Vec::new()) - } else if let Some(simp_rcvr_ty) = - simplify_type(self.tcx, rcvr_ty, SimplifyParams::Yes, StripReferences::No) + } else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, SimplifyParams::Yes) { let mut potential_candidates = Vec::new(); let mut explicitly_negative = Vec::new(); @@ -1780,12 +1790,8 @@ 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(), - SimplifyParams::Yes, - StripReferences::No, - ); + let imp_simp = + simplify_type(self.tcx, imp.self_ty(), SimplifyParams::Yes); imp_simp.map_or(false, |s| s == simp_rcvr_ty) }) { @@ -1836,7 +1842,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [] => {} [trait_info] => { let msg = format!( - "the trait `{}` defines an item `{}`, but is explicitely unimplemented", + "the trait `{}` defines an item `{}`, but is explicitly unimplemented", self.tcx.def_path_str(trait_info.def_id), item_name ); @@ -1844,7 +1850,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } trait_infos => { let mut msg = format!( - "the following traits define an item `{}`, but are explicitely unimplemented:", + "the following traits define an item `{}`, but are explicitly unimplemented:", item_name ); for trait_info in trait_infos { diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index a9e6b1caff..6e0b902a00 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -511,19 +511,15 @@ struct GeneratorTypes<'tcx> { fn get_owner_return_paths<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, -) -> Option<(hir::HirId, ReturnsVisitor<'tcx>)> { +) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let id = tcx.hir().get_parent_item(hir_id); - tcx.hir() - .find(id) - .map(|n| (id, n)) - .and_then(|(hir_id, node)| node.body_id().map(|b| (hir_id, b))) - .map(|(hir_id, body_id)| { - let body = tcx.hir().body(body_id); - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(body); - (hir_id, visitor) - }) + let parent_id = tcx.hir().get_parent_item(hir_id); + tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| { + let body = tcx.hir().body(body_id); + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(body); + (parent_id, visitor) + }) } // Forbid defining intrinsics in Rust code, @@ -566,7 +562,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId, span: S fn report_forbidden_specialization( tcx: TyCtxt<'_>, - impl_item: &hir::ImplItem<'_>, + impl_item: &hir::ImplItemRef, parent_impl: DefId, ) { let mut err = struct_span_err!( @@ -598,12 +594,12 @@ fn report_forbidden_specialization( fn missing_items_err( tcx: TyCtxt<'_>, impl_span: Span, - missing_items: &[ty::AssocItem], + missing_items: &[&ty::AssocItem], full_impl_span: Span, ) { let missing_items_msg = missing_items .iter() - .map(|trait_item| trait_item.ident.to_string()) + .map(|trait_item| trait_item.name.to_string()) .collect::>() .join("`, `"); @@ -632,7 +628,7 @@ fn missing_items_err( let msg = format!("implement the missing item: `{}`", snippet); let appl = Applicability::HasPlaceholders; if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { - err.span_label(span, format!("`{}` from trait", trait_item.ident)); + err.span_label(span, format!("`{}` from trait", trait_item.name)); err.tool_only_span_suggestion(sugg_sp, &msg, code, appl); } else { err.span_suggestion_hidden(sugg_sp, &msg, code, appl); @@ -641,6 +637,31 @@ fn missing_items_err( err.emit(); } +fn missing_items_must_implement_one_of_err( + tcx: TyCtxt<'_>, + impl_span: Span, + missing_items: &[Ident], + annotation_span: Option, +) { + let missing_items_msg = + missing_items.iter().map(Ident::to_string).collect::>().join("`, `"); + + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0046, + "not all trait items implemented, missing one of: `{}`", + missing_items_msg + ); + err.span_label(impl_span, format!("missing one of `{}` in implementation", missing_items_msg)); + + if let Some(annotation_span) = annotation_span { + err.span_note(annotation_span, "required because of this annotation"); + } + + err.emit(); +} + /// Resugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. fn bounds_from_generic_predicates<'tcx>( tcx: TyCtxt<'tcx>, @@ -695,7 +716,11 @@ fn bounds_from_generic_predicates<'tcx>( // insert the associated types where they correspond, but for now let's be "lazy" and // propose this instead of the following valid resugaring: // `T: Trait, Trait::Assoc = K` → `T: Trait` - where_clauses.push(format!("{} = {}", tcx.def_path_str(p.projection_ty.item_def_id), p.ty)); + where_clauses.push(format!( + "{} = {}", + tcx.def_path_str(p.projection_ty.item_def_id), + p.term, + )); } let where_clauses = if where_clauses.is_empty() { String::new() @@ -780,16 +805,16 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { fn_sig_suggestion( tcx, tcx.fn_sig(assoc.def_id).skip_binder(), - assoc.ident, + assoc.ident(tcx), tcx.predicates_of(assoc.def_id), assoc, ) } - ty::AssocKind::Type => format!("type {} = Type;", assoc.ident), + ty::AssocKind::Type => format!("type {} = Type;", assoc.name), ty::AssocKind::Const => { let ty = tcx.type_of(assoc.def_id); let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); - format!("const {}: {} = {};", assoc.ident, ty, val) + format!("const {}: {} = {};", assoc.name, ty, val) } } } diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 8ebfcdd539..dd49d6f489 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -221,7 +221,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, }; let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[0], }; self.apply_adjustments(lhs_expr, vec![autoref]); @@ -238,7 +238,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, }; let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), target: method.sig.inputs()[1], }; // HACK(eddyb) Bypass checks due to reborrows being in @@ -399,8 +399,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; if let Ref(_, rty, _) = lhs_ty.kind() { - if self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span) - && self.lookup_op_method(rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok() + if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span) + && self.lookup_op_method(*rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok() { if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { let msg = &format!( @@ -423,7 +423,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if let Some(missing_trait) = missing_trait { - let mut visitor = TypeParamVisitor(self.tcx, vec![]); + let mut visitor = TypeParamVisitor(vec![]); visitor.visit_ty(lhs_ty); if op.node == hir::BinOpKind::Add @@ -434,7 +434,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This has nothing here because it means we did string // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted - } else if let [ty] = &visitor.1[..] { + } else if let [ty] = &visitor.0[..] { if let ty::Param(p) = *ty.kind() { // Check if the method would be found if the type param wasn't // involved. If so, it means that adding a trait bound to the param is @@ -452,7 +452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, self.body_id, &mut err, - ty, + *ty, rhs_ty, missing_trait, p, @@ -549,16 +549,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_assign: IsAssign, op: hir::BinOp, ) -> bool { - let source_map = self.tcx.sess.source_map(); - let remove_borrow_msg = "String concatenation appends the string on the right to the \ - string on the left and may require reallocation. This \ - requires ownership of the string on the left"; - - let msg = "`to_owned()` can be used to create an owned `String` \ - from a string reference. String concatenation \ - appends the string on the right to the string \ - on the left and may require reallocation. This \ - requires ownership of the string on the left"; + let str_concat_note = "string concatenation requires an owned `String` on the left"; + let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; + let to_owned_msg = "create an owned `String` from a string reference"; let string_type = self.tcx.get_diagnostic_item(sym::String); let is_std_string = |ty: Ty<'tcx>| match ty.ty_adt_def() { @@ -574,31 +567,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) => { if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` - err.span_label( - op.span, - "`+` cannot be used to concatenate two `&str` strings", - ); - match source_map.span_to_snippet(lhs_expr.span) { - Ok(lstring) => { - err.span_suggestion( - lhs_expr.span, - if lstring.starts_with('&') { - remove_borrow_msg - } else { - msg - }, - if let Some(stripped) = lstring.strip_prefix('&') { - // let a = String::new(); - // let _ = &a + "bar"; - stripped.to_string() - } else { - format!("{}.to_owned()", lstring) - }, - Applicability::MachineApplicable, - ) - } - _ => err.help(msg), - }; + err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); + err.note(str_concat_note); + if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { + err.span_suggestion_verbose( + lhs_expr.span.until(lhs_inner_expr.span), + rm_borrow_msg, + "".to_owned(), + Applicability::MachineApplicable + ); + } else { + err.span_suggestion_verbose( + lhs_expr.span.shrink_to_hi(), + to_owned_msg, + ".to_owned()".to_owned(), + Applicability::MachineApplicable + ); + } } true } @@ -609,32 +594,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op.span, "`+` cannot be used to concatenate a `&str` with a `String`", ); - match ( - source_map.span_to_snippet(lhs_expr.span), - source_map.span_to_snippet(rhs_expr.span), - is_assign, - ) { - (Ok(l), Ok(r), IsAssign::No) => { - let to_string = if let Some(stripped) = l.strip_prefix('&') { - // let a = String::new(); let b = String::new(); - // let _ = &a + b; - stripped.to_string() + match is_assign { + IsAssign::No => { + let sugg_msg; + let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { + sugg_msg = "remove the borrow on the left and add one on the right"; + (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned()) } else { - format!("{}.to_owned()", l) + sugg_msg = "create an owned `String` on the left and add a borrow on the right"; + (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned()) }; - err.multipart_suggestion( - msg, - vec![ - (lhs_expr.span, to_string), - (rhs_expr.span, format!("&{}", r)), - ], + let suggestions = vec![ + lhs_sugg, + (rhs_expr.span.shrink_to_lo(), "&".to_owned()), + ]; + err.multipart_suggestion_verbose( + sugg_msg, + suggestions, Applicability::MachineApplicable, ); } - _ => { - err.help(msg); + IsAssign::Yes => { + err.note(str_concat_note); } - }; + } true } _ => false, @@ -895,7 +878,7 @@ enum Op { /// Dereferences a single level of immutable referencing. fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> { match ty.kind() { - ty::Ref(_, ty, hir::Mutability::Not) => ty, + ty::Ref(_, ty, hir::Mutability::Not) => *ty, _ => ty, } } @@ -972,7 +955,7 @@ fn suggest_constraining_param( if let Some(generics) = param_def_id .as_local() .map(|id| hir.local_def_id_to_hir_id(id)) - .and_then(|id| hir.find(hir.get_parent_item(id))) + .and_then(|id| hir.find_by_def_id(hir.get_parent_item(id))) .as_ref() .and_then(|node| node.generics()) { @@ -991,15 +974,12 @@ fn suggest_constraining_param( } } -struct TypeParamVisitor<'tcx>(TyCtxt<'tcx>, Vec>); +struct TypeParamVisitor<'tcx>(Vec>); impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.0) - } fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { if let ty::Param(_) = ty.kind() { - self.1.push(ty); + self.0.push(ty); } ty.super_visit_with(self) } diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index ec06e0b112..e034adde1b 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -15,7 +15,7 @@ use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; 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::symbol::{sym, Ident}; use rustc_span::{BytePos, MultiSpan, DUMMY_SP}; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::{ObligationCause, Pattern}; @@ -1029,7 +1029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_def_spans = if fields.is_empty() { vec![res_span] } else { - fields.iter().map(|f| f.ident.span).collect() + fields.iter().map(|f| f.ident(self.tcx).span).collect() }; let last_field_def_span = *field_def_spans.last().unwrap(); @@ -1231,7 +1231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .fields .iter() .enumerate() - .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) + .map(|(i, field)| (field.ident(self.tcx).normalize_to_macros_2_0(), (i, field))) .collect::>(); // Keep track of which fields have already appeared in the pattern. @@ -1272,7 +1272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut unmentioned_fields = variant .fields .iter() - .map(|field| (field, field.ident.normalize_to_macros_2_0())) + .map(|field| (field, field.ident(self.tcx).normalize_to_macros_2_0())) .filter(|(_, ident)| !used_fields.contains_key(ident)) .collect::>(); @@ -1579,7 +1579,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &[hir::PatField<'_>], variant: &VariantDef, ) -> String { - let variant_field_idents = variant.fields.iter().map(|f| f.ident).collect::>(); + let variant_field_idents = + variant.fields.iter().map(|f| f.ident(self.tcx)).collect::>(); fields .iter() .map(|field| { @@ -1934,7 +1935,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { element_ty: Ty<'tcx>, arr_ty: Ty<'tcx>, slice: Option<&'tcx Pat<'tcx>>, - len: &ty::Const<'tcx>, + len: ty::Const<'tcx>, min_len: u64, ) -> (Option>, Ty<'tcx>) { if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) { @@ -2028,16 +2029,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("the semantics of slice patterns changed recently; see issue #62254"); } } else if Autoderef::new(&self.infcx, self.param_env, self.body_id, span, expected_ty, span) - .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..))) + .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) { if let (Some(span), true) = (ti.span, ti.origin_expr) { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - err.span_suggestion( + let applicability = Autoderef::new( + &self.infcx, + self.param_env, + self.body_id, span, - "consider slicing here", - format!("{}[..]", snippet), - Applicability::MachineApplicable, - ); + self.resolve_vars_if_possible(ti.expected), + span, + ) + .find_map(|(ty, _)| { + match ty.kind() { + ty::Adt(adt_def, _) + if self.tcx.is_diagnostic_item(sym::Option, adt_def.did) + || self.tcx.is_diagnostic_item(sym::Result, adt_def.did) => + { + // Slicing won't work here, but `.as_deref()` might (issue #91328). + err.span_suggestion( + span, + "consider using `as_deref` here", + format!("{}.as_deref()", snippet), + Applicability::MaybeIncorrect, + ); + Some(None) + } + + ty::Slice(..) | ty::Array(..) => { + Some(Some(Applicability::MachineApplicable)) + } + + _ => None, + } + }) + .unwrap_or(Some(Applicability::MaybeIncorrect)); + + if let Some(applicability) = applicability { + err.span_suggestion( + span, + "consider slicing here", + format!("{}[..]", snippet), + applicability, + ); + } } } } diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index d01e21bcb2..318979b462 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -31,7 +31,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.apply_adjustments( oprnd_expr, vec![Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), + kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)), target: method.sig.inputs()[0], }], ); @@ -142,7 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if unsize { // We only unsize arrays here. if let ty::Array(element_ty, _) = adjusted_ty.kind() { - self_ty = self.tcx.mk_slice(element_ty); + self_ty = self.tcx.mk_slice(*element_ty); } else { continue; } @@ -165,9 +165,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut adjustments = self.adjust_steps(autoderef); if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), + kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)), target: self.tcx.mk_ref( - region, + *region, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty }, ), }); @@ -432,9 +432,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // not the case today. allow_two_phase_borrow: AllowTwoPhase::No, }; - adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); - adjustment.target = - self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); + adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)); + adjustment.target = self + .tcx + .mk_ref(*region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); } source = adjustment.target; } diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 1b42edc83b..513e8576f2 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -80,7 +80,7 @@ use crate::outlives::outlives_bounds::InferCtxtExt as _; use rustc_data_structures::stable_set::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::PatKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, RegionObligation, RegionckMode}; @@ -406,12 +406,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { // hierarchy, and in particular the relationships between free // regions, until regionck, as described in #3238. - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_fn( &mut self, fk: intravisit::FnKind<'tcx>, @@ -695,7 +689,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { let rptr_ty = self.resolve_node_type(id); if let ty::Ref(r, _, _) = rptr_ty.kind() { debug!("rptr_ty={}", rptr_ty); - self.link_region(span, r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed); + self.link_region(span, *r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed); } } @@ -859,15 +853,15 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.sub_regions( infer::ReborrowUpvar(span, upvar_id), borrow_region, - upvar_borrow.region, + captured_place.region.unwrap(), ); - if let ty::ImmBorrow = upvar_borrow.kind { + if let ty::ImmBorrow = upvar_borrow { debug!("link_upvar_region: capture by shared ref"); } else { all_captures_are_imm_borrow = false; } } - ty::UpvarCapture::ByValue(_) => { + ty::UpvarCapture::ByValue => { all_captures_are_imm_borrow = false; } } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index ffd7d29bbb..949d857bff 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -33,12 +33,11 @@ use super::FnCtxt; use crate::expr_use_visitor as euv; -use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; use rustc_middle::mir::FakeReadCause; @@ -72,7 +71,7 @@ enum PlaceAncestryRelation { /// 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>>; +type InferredCaptureInformation<'tcx> = Vec<(Place<'tcx>, ty::CaptureInfo)>; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { @@ -141,12 +140,6 @@ struct InferBorrowKindVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { hir::ExprKind::Closure(cc, _, body_id, _, _) => { @@ -207,8 +200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert_eq!(body_owner_def_id.to_def_id(), closure_def_id); let mut delegate = InferBorrowKind { fcx: self, - closure_def_id, - closure_span: span, + closure_def_id: local_def_id, capture_information: Default::default(), fake_reads: Default::default(), }; @@ -231,7 +223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (capture_information, closure_kind, origin) = self .process_collected_capture_information(capture_clause, delegate.capture_information); - self.compute_min_captures(closure_def_id, capture_information); + self.compute_min_captures(closure_def_id, capture_information, span); let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); @@ -252,21 +244,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("seed place {:?}", place); - let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id); - let capture_kind = - self.init_capture_kind_for_place(&place, capture_clause, upvar_id, span); + let capture_kind = self.init_capture_kind_for_place(&place, capture_clause); let fake_info = ty::CaptureInfo { capture_kind_expr_id: None, path_expr_id: None, capture_kind, }; - capture_information.insert(place, fake_info); + capture_information.push((place, fake_info)); } } // This will update the min captures based on this new fake information. - self.compute_min_captures(closure_def_id, capture_information); + self.compute_min_captures(closure_def_id, capture_information, span); } let before_feature_tys = self.final_upvar_tys(closure_def_id); @@ -362,7 +352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { captured_place.place, upvar_ty, capture, captured_place.mutability, ); - apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture) + apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region) }) .collect() } @@ -387,77 +377,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause: hir::CaptureBy, capture_information: InferredCaptureInformation<'tcx>, ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) { - let mut processed: InferredCaptureInformation<'tcx> = Default::default(); - let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM; let mut origin: Option<(Span, Place<'tcx>)> = None; - for (place, mut capture_info) in capture_information { - // Apply rules for safety before inferring closure kind - let (place, capture_kind) = - restrict_capture_precision(place, capture_info.capture_kind); - capture_info.capture_kind = capture_kind; + let processed = capture_information + .into_iter() + .map(|(place, mut capture_info)| { + // Apply rules for safety before inferring closure kind + let (place, capture_kind) = + restrict_capture_precision(place, capture_info.capture_kind); - let (place, capture_kind) = - truncate_capture_for_optimization(place, capture_info.capture_kind); - capture_info.capture_kind = capture_kind; + let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind); - let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { - self.tcx.hir().span(usage_expr) - } else { - unreachable!() - }; + let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { + self.tcx.hir().span(usage_expr) + } else { + unreachable!() + }; - let updated = match capture_info.capture_kind { - ty::UpvarCapture::ByValue(..) => match closure_kind { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { - (ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) - } - // If closure is already FnOnce, don't update - ty::ClosureKind::FnOnce => (closure_kind, origin), - }, + let updated = match capture_kind { + ty::UpvarCapture::ByValue => match closure_kind { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { + (ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) + } + // If closure is already FnOnce, don't update + ty::ClosureKind::FnOnce => (closure_kind, origin.take()), + }, - ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, - .. - }) => { - match closure_kind { - ty::ClosureKind::Fn => { - (ty::ClosureKind::FnMut, Some((usage_span, place.clone()))) + ty::UpvarCapture::ByRef( + ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, + ) => { + match closure_kind { + ty::ClosureKind::Fn => { + (ty::ClosureKind::FnMut, Some((usage_span, place.clone()))) + } + // Don't update the origin + ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => { + (closure_kind, origin.take()) + } } - // Don't update the origin - ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => (closure_kind, origin), } - } - - _ => (closure_kind, origin), - }; - closure_kind = updated.0; - origin = updated.1; + _ => (closure_kind, origin.take()), + }; - let (place, capture_kind) = match capture_clause { - hir::CaptureBy::Value => adjust_for_move_closure(place, capture_info.capture_kind), - hir::CaptureBy::Ref => { - adjust_for_non_move_closure(place, capture_info.capture_kind) - } - }; + closure_kind = updated.0; + origin = updated.1; - // This restriction needs to be applied after we have handled adjustments for `move` - // closures. We want to make sure any adjustment that might make us move the place into - // the closure gets handled. - let (place, capture_kind) = - restrict_precision_for_drop_types(self, place, capture_kind, usage_span); + let (place, capture_kind) = match capture_clause { + hir::CaptureBy::Value => adjust_for_move_closure(place, capture_kind), + hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind), + }; - capture_info.capture_kind = capture_kind; + // This restriction needs to be applied after we have handled adjustments for `move` + // closures. We want to make sure any adjustment that might make us move the place into + // the closure gets handled. + let (place, capture_kind) = + restrict_precision_for_drop_types(self, place, capture_kind, usage_span); - let capture_info = if let Some(existing) = processed.get(&place) { - determine_capture_info(*existing, capture_info) - } else { - capture_info - }; - processed.insert(place, capture_info); - } + capture_info.capture_kind = capture_kind; + (place, capture_info) + }) + .collect(); (processed, closure_kind, origin) } @@ -535,6 +516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, closure_def_id: DefId, capture_information: InferredCaptureInformation<'tcx>, + closure_span: Span, ) { if capture_information.is_empty() { return; @@ -554,8 +536,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { let mutability = self.determine_capture_mutability(&typeck_results, &place); - let min_cap_list = - vec![ty::CapturedPlace { place, info: capture_info, mutability }]; + let min_cap_list = vec![ty::CapturedPlace { + place, + info: capture_info, + mutability, + region: None, + }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; } @@ -608,8 +594,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !descendant_found { for possible_ancestor in min_cap_list.iter_mut() { match determine_place_ancestry_relation(&place, &possible_ancestor.place) { + PlaceAncestryRelation::SamePlace => { + ancestor_found = true; + possible_ancestor.info = determine_capture_info( + possible_ancestor.info, + updated_capture_info, + ); + + // Only one related place will be in the list. + break; + } // current place is descendant of possible_ancestor - PlaceAncestryRelation::Descendant | PlaceAncestryRelation::SamePlace => { + PlaceAncestryRelation::Descendant => { ancestor_found = true; let backup_path_expr_id = possible_ancestor.info.path_expr_id; @@ -629,7 +625,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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. + // Only one related place will be in the list. break; } _ => {} @@ -640,12 +636,31 @@ 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, info: updated_capture_info, mutability }; + let captured_place = ty::CapturedPlace { + place, + info: updated_capture_info, + mutability, + region: None, + }; min_cap_list.push(captured_place); } } + // For each capture that is determined to be captured by ref, add region info. + for (_, captures) in &mut root_var_min_capture_list { + for capture in captures { + match capture.info.capture_kind { + ty::UpvarCapture::ByRef(_) => { + let PlaceBase::Upvar(upvar_id) = capture.place.base else { bug!("expected upvar") }; + let origin = UpvarRegion(upvar_id, closure_span); + let upvar_region = self.next_region_var(origin); + capture.region = Some(upvar_region); + } + _ => (), + } + } + } + debug!( "For closure={:?}, min_captures before sorting={:?}", closure_def_id, root_var_min_capture_list @@ -947,7 +962,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { max_capture_info = determine_capture_info(max_capture_info, capture.info); } - apply_capture_kind_on_capture_ty(self.tcx, ty, max_capture_info.capture_kind) + apply_capture_kind_on_capture_ty( + self.tcx, + ty, + max_capture_info.capture_kind, + Some(self.tcx.lifetimes.re_erased), + ) } }; @@ -977,6 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, capture.place.ty(), capture.info.capture_kind, + Some(self.tcx.lifetimes.re_erased), ); // Checks if a capture implements any of the auto traits @@ -1086,7 +1107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for captured_place in root_var_min_capture_list.iter() { match captured_place.info.capture_kind { // Only care about captures that are moved into the closure - ty::UpvarCapture::ByValue(..) => { + ty::UpvarCapture::ByValue => { projections_list.push(captured_place.place.projections.as_slice()); diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise { source_expr: captured_place.info.path_expr_id, @@ -1470,9 +1491,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, place: &Place<'tcx>, capture_clause: hir::CaptureBy, - upvar_id: ty::UpvarId, - closure_span: Span, - ) -> ty::UpvarCapture<'tcx> { + ) -> ty::UpvarCapture { match capture_clause { // In case of a move closure if the data is accessed through a reference we // want to capture by ref to allow precise capture using reborrows. @@ -1480,15 +1499,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the data will be moved out of this place, then the place will be truncated // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into // the closure. - hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => { - ty::UpvarCapture::ByValue(None) - } - hir::CaptureBy::Value | hir::CaptureBy::Ref => { - let origin = UpvarRegion(upvar_id, closure_span); - let upvar_region = self.next_region_var(origin); - let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; - ty::UpvarCapture::ByRef(upvar_borrow) + hir::CaptureBy::Value if !place.deref_tys().any(Ty::is_ref) => { + ty::UpvarCapture::ByValue } + hir::CaptureBy::Value | hir::CaptureBy::Ref => ty::UpvarCapture::ByRef(ty::ImmBorrow), } } @@ -1513,7 +1527,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn log_capture_analysis_first_pass( &self, closure_def_id: rustc_hir::def_id::DefId, - capture_information: &FxIndexMap, ty::CaptureInfo<'tcx>>, + capture_information: &InferredCaptureInformation<'tcx>, closure_span: Span, ) { if self.should_log_capture_analysis(closure_def_id) { @@ -1629,9 +1643,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn restrict_repr_packed_field_ref_capture<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - place: &Place<'tcx>, - mut curr_borrow_kind: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut place: Place<'tcx>, + mut curr_borrow_kind: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let pos = place.projections.iter().enumerate().position(|(i, p)| { let ty = place.ty_before_projection(i); @@ -1639,7 +1653,8 @@ fn restrict_repr_packed_field_ref_capture<'tcx>( match p.kind { ProjectionKind::Field(..) => match ty.kind() { ty::Adt(def, _) if def.repr.packed() => { - match tcx.layout_of(param_env.and(p.ty)) { + // We erase regions here because they cannot be hashed + match tcx.layout_of(param_env.and(tcx.erase_regions(p.ty))) { Ok(layout) if layout.align.abi.bytes() == 1 => { // if the alignment is 1, the type can't be further // disaligned. @@ -1662,8 +1677,6 @@ fn restrict_repr_packed_field_ref_capture<'tcx>( } }); - let mut place = place.clone(); - if let Some(pos) = pos { truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_borrow_kind, pos); } @@ -1675,12 +1688,14 @@ fn restrict_repr_packed_field_ref_capture<'tcx>( fn apply_capture_kind_on_capture_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - capture_kind: UpvarCapture<'tcx>, + capture_kind: UpvarCapture, + region: Option>, ) -> Ty<'tcx> { match capture_kind { - ty::UpvarCapture::ByValue(_) => ty, - ty::UpvarCapture::ByRef(borrow) => tcx - .mk_ref(borrow.region, ty::TypeAndMut { ty: ty, mutbl: borrow.kind.to_mutbl_lossy() }), + ty::UpvarCapture::ByValue => ty, + ty::UpvarCapture::ByRef(kind) => { + tcx.mk_ref(region.unwrap(), ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() }) + } } } @@ -1708,9 +1723,7 @@ struct InferBorrowKind<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, // The def-id of the closure whose kind and upvar accesses are being inferred. - closure_def_id: DefId, - - closure_span: Span, + closure_def_id: LocalDefId, /// 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. @@ -1742,184 +1755,38 @@ struct InferBorrowKind<'a, 'tcx> { fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, } -impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { - #[instrument(skip(self), level = "debug")] - fn adjust_upvar_borrow_kind_for_consume( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - let tcx = self.fcx.tcx; - let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { - return; - }; - - debug!(?upvar_id); - - let usage_span = tcx.hir().span(diag_expr_id); - - let capture_info = ty::CaptureInfo { - capture_kind_expr_id: Some(diag_expr_id), - path_expr_id: Some(diag_expr_id), - capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)), - }; - - let curr_info = self.capture_information[&place_with_id.place]; - let updated_info = determine_capture_info(curr_info, capture_info); - - self.capture_information[&place_with_id.place] = updated_info; - } - - /// Indicates that `place_with_id` is being directly mutated (e.g., assigned - /// to). If the place is based on a by-ref upvar, this implies that - /// the upvar must be borrowed using an `&mut` borrow. - #[instrument(skip(self), level = "debug")] - fn adjust_upvar_borrow_kind_for_mut( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - if let PlaceBase::Upvar(_) = place_with_id.place.base { - // Raw pointers don't inherit mutability - if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { - return; - } - self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::MutBorrow); - } - } - - #[instrument(skip(self), level = "debug")] - fn adjust_upvar_borrow_kind_for_unique( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - if let PlaceBase::Upvar(_) = place_with_id.place.base { - if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { - // Raw pointers don't inherit mutability. - return; - } - // for a borrowed pointer to be unique, its base must be unique - self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::UniqueImmBorrow); - } - } - - fn adjust_upvar_deref( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - borrow_kind: ty::BorrowKind, - ) { - assert!(match borrow_kind { - ty::MutBorrow => true, - ty::UniqueImmBorrow => true, - - // imm borrows never require adjusting any kinds, so we don't wind up here - ty::ImmBorrow => false, - }); - - // if this is an implicit deref of an - // upvar, then we need to modify the - // borrow_kind of the upvar to make sure it - // is inferred to mutable if necessary - self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind); - } - - /// We infer the borrow_kind with which to borrow upvars in a stack closure. - /// The borrow_kind basically follows a lattice of `imm < unique-imm < mut`, - /// moving from left to right as needed (but never right to left). - /// Here the argument `mutbl` is the borrow_kind that is required by - /// some particular use. - #[instrument(skip(self), level = "debug")] - fn adjust_upvar_borrow_kind( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - kind: ty::BorrowKind, - ) { - let curr_capture_info = self.capture_information[&place_with_id.place]; - - debug!(?curr_capture_info); - - if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind { - // It's already captured by value, we don't need to do anything here - return; - } else if let ty::UpvarCapture::ByRef(curr_upvar_borrow) = curr_capture_info.capture_kind { - // Use the same region as the current capture information - // Doesn't matter since only one of the UpvarBorrow will be used. - let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region }; - - let capture_info = ty::CaptureInfo { - 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); - self.capture_information[&place_with_id.place] = updated_info; - }; - } - - #[instrument(skip(self, diag_expr_id), level = "debug")] - fn init_capture_info_for_place( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - ) { - if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { - assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id); - - // Initialize to ImmBorrow - // We will escalate the CaptureKind based on any uses we see or in `process_collected_capture_information`. - let origin = UpvarRegion(upvar_id, self.closure_span); - let upvar_region = self.fcx.next_region_var(origin); - let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; - let capture_kind = ty::UpvarCapture::ByRef(upvar_borrow); - - let expr_id = Some(diag_expr_id); - 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); - - self.capture_information.insert(place_with_id.place.clone(), capture_info); - } else { - debug!("Not upvar"); - } - } -} - impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) { - if let PlaceBase::Upvar(_) = place.base { - // We need to restrict Fake Read precision to avoid fake reading unsafe code, - // such as deref of a raw pointer. - let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::ImmBorrow, - region: &ty::ReErased, - }); + let PlaceBase::Upvar(_) = place.base else { return }; - let (place, _) = restrict_capture_precision(place, dummy_capture_kind); + // We need to restrict Fake Read precision to avoid fake reading unsafe code, + // such as deref of a raw pointer. + let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow); - let (place, _) = restrict_repr_packed_field_ref_capture( - self.fcx.tcx, - self.fcx.param_env, - &place, - dummy_capture_kind, - ); - self.fake_reads.push((place, cause, diag_expr_id)); - } + let (place, _) = restrict_capture_precision(place, dummy_capture_kind); + + let (place, _) = restrict_repr_packed_field_ref_capture( + self.fcx.tcx, + self.fcx.param_env, + place, + dummy_capture_kind, + ); + self.fake_reads.push((place, cause, diag_expr_id)); } #[instrument(skip(self), level = "debug")] fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { - if !self.capture_information.contains_key(&place_with_id.place) { - self.init_capture_info_for_place(place_with_id, diag_expr_id); - } + let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; + assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); - self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id); + self.capture_information.push(( + place_with_id.place.clone(), + ty::CaptureInfo { + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByValue, + }, + )); } #[instrument(skip(self), level = "debug")] @@ -1929,40 +1796,35 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { diag_expr_id: hir::HirId, bk: ty::BorrowKind, ) { + let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; + assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); + // The region here will get discarded/ignored - let dummy_capture_kind = - ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: bk, region: &ty::ReErased }); + let capture_kind = ty::UpvarCapture::ByRef(bk); // We only want repr packed restriction to be applied to reading references into a packed // struct, and not when the data is being moved. Therefore we call this method here instead // of in `restrict_capture_precision`. - let (place, updated_kind) = restrict_repr_packed_field_ref_capture( + let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture( self.fcx.tcx, self.fcx.param_env, - &place_with_id.place, - dummy_capture_kind, + place_with_id.place.clone(), + capture_kind, ); - let place_with_id = PlaceWithHirId { place, ..*place_with_id }; - - if !self.capture_information.contains_key(&place_with_id.place) { - self.init_capture_info_for_place(&place_with_id, diag_expr_id); + // Raw pointers don't inherit mutability + if place_with_id.place.deref_tys().any(Ty::is_unsafe_ptr) { + capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow); } - match updated_kind { - ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind, .. }) => match kind { - ty::ImmBorrow => {} - ty::UniqueImmBorrow => { - self.adjust_upvar_borrow_kind_for_unique(&place_with_id, diag_expr_id); - } - ty::MutBorrow => { - self.adjust_upvar_borrow_kind_for_mut(&place_with_id, diag_expr_id); - } + self.capture_information.push(( + place, + ty::CaptureInfo { + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), + capture_kind, }, - - // Just truncating the place will never cause capture kind to be updated to ByValue - ty::UpvarCapture::ByValue(..) => unreachable!(), - } + )); } #[instrument(skip(self), level = "debug")] @@ -1975,12 +1837,12 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn restrict_precision_for_drop_types<'a, 'tcx>( fcx: &'a FnCtxt<'a, 'tcx>, mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture<'tcx>, + mut curr_mode: ty::UpvarCapture, span: Span, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { +) -> (Place<'tcx>, ty::UpvarCapture) { let is_copy_type = fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty(), span); - if let (false, UpvarCapture::ByValue(..)) = (is_copy_type, curr_mode) { + if let (false, UpvarCapture::ByValue) = (is_copy_type, curr_mode) { for i in 0..place.projections.len() { match place.ty_before_projection(i).kind() { ty::Adt(def, _) if def.destructor(fcx.tcx).is_some() => { @@ -2001,8 +1863,8 @@ fn restrict_precision_for_drop_types<'a, 'tcx>( /// - No projections are applied on top of Union ADTs, since these require unsafe blocks. fn restrict_precision_for_unsafe<'tcx>( mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut curr_mode: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { if place.base_ty.is_unsafe_ptr() { truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0); } @@ -2034,8 +1896,8 @@ fn restrict_precision_for_unsafe<'tcx>( /// Returns the truncated place and updated cature mode. fn restrict_capture_precision<'tcx>( place: Place<'tcx>, - curr_mode: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + curr_mode: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let (mut place, mut curr_mode) = restrict_precision_for_unsafe(place, curr_mode); if place.projections.is_empty() { @@ -2062,30 +1924,28 @@ fn restrict_capture_precision<'tcx>( /// Truncate deref of any reference. fn adjust_for_move_closure<'tcx>( mut place: Place<'tcx>, - mut kind: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut kind: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); if let Some(idx) = first_deref { truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); } - // AMAN: I think we don't need the span inside the ByValue anymore - // we have more detailed span in CaptureInfo - (place, ty::UpvarCapture::ByValue(None)) + (place, ty::UpvarCapture::ByValue) } /// Adjust closure capture just that if taking ownership of data, only move data /// from enclosing stack frame. fn adjust_for_non_move_closure<'tcx>( mut place: Place<'tcx>, - mut kind: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut kind: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let contains_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); match kind { - ty::UpvarCapture::ByValue(..) => { + ty::UpvarCapture::ByValue => { if let Some(idx) = contains_deref { truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); } @@ -2123,13 +1983,13 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String fn construct_capture_kind_reason_string<'tcx>( tcx: TyCtxt<'_>, place: &Place<'tcx>, - capture_info: &ty::CaptureInfo<'tcx>, + capture_info: &ty::CaptureInfo, ) -> 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), + ty::UpvarCapture::ByValue => "ByValue".into(), + ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind), }; format!("{} captured as {} here", place_str, capture_kind_str) @@ -2144,13 +2004,13 @@ fn construct_path_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { fn construct_capture_info_string<'tcx>( tcx: TyCtxt<'_>, place: &Place<'tcx>, - capture_info: &ty::CaptureInfo<'tcx>, + capture_info: &ty::CaptureInfo, ) -> 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), + ty::UpvarCapture::ByValue => "ByValue".into(), + ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind), }; format!("{} -> {}", place_str, capture_kind_str) } @@ -2233,25 +2093,16 @@ fn migration_suggestion_for_2229( /// would've already handled `E1`, and have an existing capture_information for it. /// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return /// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics. -fn determine_capture_info<'tcx>( - capture_info_a: ty::CaptureInfo<'tcx>, - capture_info_b: ty::CaptureInfo<'tcx>, -) -> ty::CaptureInfo<'tcx> { +fn determine_capture_info( + capture_info_a: ty::CaptureInfo, + capture_info_b: ty::CaptureInfo, +) -> ty::CaptureInfo { // If the capture kind is equivalent then, we don't need to escalate and can compare the // expressions. let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => { - // We don't need to worry about the spans being ignored here. - // - // The expr_id in capture_info corresponds to the span that is stored within - // ByValue(span) and therefore it gets handled with priortizing based on - // expressions below. - true - } - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - ref_a.kind == ref_b.kind - } - (ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false, + (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b, + (ty::UpvarCapture::ByValue, _) | (ty::UpvarCapture::ByRef(_), _) => false, }; if eq_capture_kind { @@ -2263,10 +2114,10 @@ fn determine_capture_info<'tcx>( // We select the CaptureKind which ranks higher based the following priority order: // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue(_), _) => capture_info_a, - (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, + (ty::UpvarCapture::ByValue, _) => capture_info_a, + (_, ty::UpvarCapture::ByValue) => capture_info_b, (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - match (ref_a.kind, ref_b.kind) { + match (ref_a, ref_b) { // Take LHS: (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, @@ -2294,7 +2145,7 @@ fn determine_capture_info<'tcx>( /// contained `Deref` of `&mut`. fn truncate_place_to_len_and_update_capture_kind<'tcx>( place: &mut Place<'tcx>, - curr_mode: &mut ty::UpvarCapture<'tcx>, + curr_mode: &mut ty::UpvarCapture, len: usize, ) { let is_mut_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Mut)); @@ -2304,22 +2155,19 @@ fn truncate_place_to_len_and_update_capture_kind<'tcx>( // Note that if the place contained Deref of a raw pointer it would've not been MutBorrow, so // we don't need to worry about that case here. match curr_mode { - ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: ty::BorrowKind::MutBorrow, region }) => { + ty::UpvarCapture::ByRef(ty::BorrowKind::MutBorrow) => { for i in len..place.projections.len() { if place.projections[i].kind == ProjectionKind::Deref && is_mut_ref(place.ty_before_projection(i)) { - *curr_mode = ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::UniqueImmBorrow, - region, - }); + *curr_mode = ty::UpvarCapture::ByRef(ty::BorrowKind::UniqueImmBorrow); break; } } } ty::UpvarCapture::ByRef(..) => {} - ty::UpvarCapture::ByValue(..) => {} + ty::UpvarCapture::ByValue => {} } place.projections.truncate(len); @@ -2390,8 +2238,8 @@ fn determine_place_ancestry_relation<'tcx>( /// ``` fn truncate_capture_for_optimization<'tcx>( mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture<'tcx>, -) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + mut curr_mode: ty::UpvarCapture, +) -> (Place<'tcx>, ty::UpvarCapture) { let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)); // Find the right-most deref (if any). All the projections that come after this diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 7c4f5d16ab..55757251e2 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -3,7 +3,7 @@ use crate::check::{FnCtxt, Inherited}; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -14,9 +14,10 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::outlives::obligations::TypeOutlives; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::{self, RegionckMode, SubregionOrigin}; -use rustc_middle::hir::map as hir_map; +use rustc_infer::infer::region_constraints::GenericKind; +use rustc_infer::infer::{self, RegionckMode}; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ @@ -205,7 +206,7 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_object_unsafe_self_trait_by_name(tcx, trait_item); check_associated_item(tcx, trait_item.def_id, span, method_sig); - let encl_trait_def_id = tcx.hir().get_parent_did(hir_id); + let encl_trait_def_id = tcx.hir().get_parent_item(hir_id); let encl_trait = tcx.hir().expect_item(encl_trait_def_id); let encl_trait_def_id = encl_trait.def_id.to_def_id(); let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() { @@ -257,236 +258,186 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { .emit(); } } - - check_gat_where_clauses(tcx, trait_item, encl_trait_def_id); } /// Require that the user writes where clauses on GATs for the implicit /// outlives bounds involving trait parameters in trait functions and /// lifetimes passed as GAT substs. See `self-outlives-lint` test. /// -/// This trait will be our running example. We are currently WF checking the `Item` item... -/// -/// ```rust -/// trait LendingIterator { -/// type Item<'me>; // <-- WF checking this trait item -/// -/// fn next<'a>(&'a mut self) -> Option>; +/// We use the following trait as an example throughout this function: +/// ```rust,ignore (this code fails due to this lint) +/// trait IntoIter { +/// type Iter<'a>: Iterator>; +/// type Item<'a>; +/// fn into_iter<'a>(&'a self) -> Self::Iter<'a>; /// } /// ``` -fn check_gat_where_clauses( - tcx: TyCtxt<'_>, - trait_item: &hir::TraitItem<'_>, - encl_trait_def_id: DefId, -) { - let item = tcx.associated_item(trait_item.def_id); - // If the current trait item isn't a type, it isn't a GAT - if !matches!(item.kind, ty::AssocKind::Type) { - return; - } - let generics: &ty::Generics = tcx.generics_of(trait_item.def_id); - // If the current associated type doesn't have any (own) params, it's not a GAT - // FIXME(jackh726): we can also warn in the more general case - if generics.params.len() == 0 { - return; - } - let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id); - let mut clauses: Option>> = None; - // For every function in this trait... - // In our example, this would be the `next` method - for item in - associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn)) - { - // The clauses we that we would require from this function - let mut function_clauses = FxHashSet::default(); - - let id = hir::HirId::make_owner(item.def_id.expect_local()); - let param_env = tcx.param_env(item.def_id.expect_local()); - - let sig = tcx.fn_sig(item.def_id); - // Get the signature using placeholders. In our example, this would - // convert the late-bound 'a into a free region. - let sig = tcx.liberate_late_bound_regions(item.def_id, sig); - // Collect the arguments that are given to this GAT in the return type - // of the function signature. In our example, the GAT in the return - // type is `::Item<'a>`, so 'a and Self are arguments. - let (regions, types) = - GATSubstCollector::visit(tcx, trait_item.def_id.to_def_id(), sig.output()); - - // If both regions and types are empty, then this GAT isn't in the - // return type, and we shouldn't try to do clause analysis - // (particularly, doing so would end up with an empty set of clauses, - // since the current method would require none, and we take the - // intersection of requirements of all methods) - if types.is_empty() && regions.is_empty() { - continue; - } - - // The types we can assume to be well-formed. In our example, this - // would be &'a mut Self, from the first argument. - let mut wf_tys = FxHashSet::default(); - wf_tys.extend(sig.inputs()); - - // For each region argument (e.g., 'a in our example), check for a - // relationship to the type arguments (e.g., Self). If there is an - // outlives relationship (`Self: 'a`), then we want to ensure that is - // reflected in a where clause on the GAT itself. - for (region, region_idx) in ®ions { - for (ty, ty_idx) in &types { - // In our example, requires that Self: 'a - if ty_known_to_outlive(tcx, id, param_env, &wf_tys, *ty, *region) { - debug!(?ty_idx, ?region_idx); - debug!("required clause: {} must outlive {}", ty, region); - // Translate into the generic parameters of the GAT. In - // our example, the type was Self, which will also be - // Self in the GAT. - let ty_param = generics.param_at(*ty_idx, tcx); - let ty_param = tcx.mk_ty(ty::Param(ty::ParamTy { - index: ty_param.index, - name: ty_param.name, - })); - // Same for the region. In our example, 'a corresponds - // to the 'me parameter. - let region_param = generics.param_at(*region_idx, tcx); - let region_param = - tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { - def_id: region_param.def_id, - index: region_param.index, - name: region_param.name, - })); - // The predicate we expect to see. (In our example, - // `Self: 'me`.) - let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( - ty_param, - region_param, - )); - let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); - function_clauses.insert(clause); - } +fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRef]) { + // Associates every GAT's def_id to a list of possibly missing bounds detected by this lint. + let mut required_bounds_by_item = FxHashMap::default(); + + // Loop over all GATs together, because if this lint suggests adding a where-clause bound + // to one GAT, it might then require us to an additional bound on another GAT. + // In our `IntoIter` example, we discover a missing `Self: 'a` bound on `Iter<'a>`, which + // then in a second loop adds a `Self: 'a` bound to `Item` due to the relationship between + // those GATs. + loop { + let mut should_continue = false; + for gat_item in associated_items { + let gat_def_id = gat_item.id.def_id; + let gat_item = tcx.associated_item(gat_def_id); + // If this item is not an assoc ty, or has no substs, then it's not a GAT + if gat_item.kind != ty::AssocKind::Type { + continue; + } + let gat_generics = tcx.generics_of(gat_def_id); + // FIXME(jackh726): we can also warn in the more general case + if gat_generics.params.is_empty() { + continue; } - } - // For each region argument (e.g., 'a in our example), also check for a - // relationship to the other region arguments. If there is an - // outlives relationship, then we want to ensure that is - // reflected in a where clause on the GAT itself. - for (region_a, region_a_idx) in ®ions { - for (region_b, region_b_idx) in ®ions { - if region_a == region_b { + // Gather the bounds with which all other items inside of this trait constrain the GAT. + // This is calculated by taking the intersection of the bounds that each item + // constrains the GAT with individually. + let mut new_required_bounds: Option>> = None; + for item in associated_items { + let item_def_id = item.id.def_id; + // Skip our own GAT, since it does not constrain itself at all. + if item_def_id == gat_def_id { continue; } - if region_known_to_outlive(tcx, id, param_env, &wf_tys, *region_a, *region_b) { - debug!(?region_a_idx, ?region_b_idx); - debug!("required clause: {} must outlive {}", region_a, region_b); - // Translate into the generic parameters of the GAT. - let region_a_param = generics.param_at(*region_a_idx, tcx); - let region_a_param = - tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { - def_id: region_a_param.def_id, - index: region_a_param.index, - name: region_a_param.name, - })); - // Same for the region. - let region_b_param = generics.param_at(*region_b_idx, tcx); - let region_b_param = - tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { - def_id: region_b_param.def_id, - index: region_b_param.index, - name: region_b_param.name, - })); - // The predicate we expect to see. - let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( - region_a_param, - region_b_param, - )); - let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); - function_clauses.insert(clause); + let item_hir_id = item.id.hir_id(); + let param_env = tcx.param_env(item_def_id); + + let item_required_bounds = match item.kind { + // In our example, this corresponds to `into_iter` method + hir::AssocItemKind::Fn { .. } => { + // For methods, we check the function signature's return type for any GATs + // to constrain. In the `into_iter` case, we see that the return type + // `Self::Iter<'a>` is a GAT we want to gather any potential missing bounds from. + let sig: ty::FnSig<'_> = tcx.liberate_late_bound_regions( + item_def_id.to_def_id(), + tcx.fn_sig(item_def_id), + ); + gather_gat_bounds( + tcx, + param_env, + item_hir_id, + sig.output(), + // We also assume that all of the function signature's parameter types + // are well formed. + &sig.inputs().iter().copied().collect(), + gat_def_id, + gat_generics, + ) + } + // In our example, this corresponds to the `Iter` and `Item` associated types + hir::AssocItemKind::Type => { + // If our associated item is a GAT with missing bounds, add them to + // the param-env here. This allows this GAT to propagate missing bounds + // to other GATs. + let param_env = augment_param_env( + tcx, + param_env, + required_bounds_by_item.get(&item_def_id), + ); + gather_gat_bounds( + tcx, + param_env, + item_hir_id, + tcx.explicit_item_bounds(item_def_id) + .iter() + .copied() + .collect::>(), + &FxHashSet::default(), + gat_def_id, + gat_generics, + ) + } + hir::AssocItemKind::Const => None, + }; + + if let Some(item_required_bounds) = item_required_bounds { + // Take the intersection of the required bounds for this GAT, and + // the item_required_bounds which are the ones implied by just + // this item alone. + // This is why we use an Option<_>, since we need to distinguish + // the empty set of bounds from the _uninitialized_ set of bounds. + if let Some(new_required_bounds) = &mut new_required_bounds { + new_required_bounds.retain(|b| item_required_bounds.contains(b)); + } else { + new_required_bounds = Some(item_required_bounds); + } } } - } - // Imagine we have: - // ``` - // trait Foo { - // type Bar<'me>; - // fn gimme(&self) -> Self::Bar<'_>; - // fn gimme_default(&self) -> Self::Bar<'static>; - // } - // ``` - // We only want to require clauses on `Bar` that we can prove from *all* functions (in this - // case, `'me` can be `static` from `gimme_default`) - match clauses.as_mut() { - Some(clauses) => { - clauses.drain_filter(|p| !function_clauses.contains(p)); - } - None => { - clauses = Some(function_clauses); + if let Some(new_required_bounds) = new_required_bounds { + let required_bounds = required_bounds_by_item.entry(gat_def_id).or_default(); + if new_required_bounds.into_iter().any(|p| required_bounds.insert(p)) { + // Iterate until our required_bounds no longer change + // Since they changed here, we should continue the loop + should_continue = true; + } } } + // We know that this loop will eventually halt, since we only set `should_continue` if the + // `required_bounds` for this item grows. Since we are not creating any new region or type + // variables, the set of all region and type bounds that we could ever insert are limited + // by the number of unique types and regions we observe in a given item. + if !should_continue { + break; + } } - // If there are any clauses that aren't provable, emit an error - let clauses = clauses.unwrap_or_default(); - debug!(?clauses); - if !clauses.is_empty() { - let param_env = tcx.param_env(trait_item.def_id); + for (gat_def_id, required_bounds) in required_bounds_by_item { + let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id); + debug!(?required_bounds); + let param_env = tcx.param_env(gat_def_id); + let gat_hir = gat_item_hir.hir_id(); - let mut clauses: Vec<_> = clauses + let mut unsatisfied_bounds: Vec<_> = required_bounds .into_iter() .filter(|clause| match clause.kind().skip_binder() { ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => { - !region_known_to_outlive( - tcx, - trait_item.hir_id(), - param_env, - &FxHashSet::default(), - a, - b, - ) + !region_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b) } ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { - !ty_known_to_outlive( - tcx, - trait_item.hir_id(), - param_env, - &FxHashSet::default(), - a, - b, - ) + !ty_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b) } _ => bug!("Unexpected PredicateKind"), }) - .map(|clause| format!("{}", clause)) + .map(|clause| clause.to_string()) .collect(); // We sort so that order is predictable - clauses.sort(); + unsatisfied_bounds.sort(); - if !clauses.is_empty() { - let plural = if clauses.len() > 1 { "s" } else { "" }; + if !unsatisfied_bounds.is_empty() { + let plural = if unsatisfied_bounds.len() > 1 { "s" } else { "" }; let mut err = tcx.sess.struct_span_err( - trait_item.span, - &format!("missing required bound{} on `{}`", plural, trait_item.ident), + gat_item_hir.span, + &format!("missing required bound{} on `{}`", plural, gat_item_hir.ident), ); let suggestion = format!( "{} {}", - if !trait_item.generics.where_clause.predicates.is_empty() { + if !gat_item_hir.generics.where_clause.predicates.is_empty() { "," } else { " where" }, - clauses.join(", "), + unsatisfied_bounds.join(", "), ); err.span_suggestion( - trait_item.generics.where_clause.tail_span_for_suggestion(), + gat_item_hir.generics.where_clause.tail_span_for_suggestion(), &format!("add the required where clause{}", plural), suggestion, Applicability::MachineApplicable, ); - let bound = if clauses.len() > 1 { "these bounds are" } else { "this bound is" }; + let bound = + if unsatisfied_bounds.len() > 1 { "these bounds are" } else { "this bound is" }; err.note(&format!( "{} currently required to ensure that impls have maximum flexibility", bound @@ -502,7 +453,142 @@ fn check_gat_where_clauses( } } -// FIXME(jackh726): refactor some of the shared logic between the two functions below +/// Add a new set of predicates to the caller_bounds of an existing param_env. +fn augment_param_env<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + new_predicates: Option<&FxHashSet>>, +) -> ty::ParamEnv<'tcx> { + let Some(new_predicates) = new_predicates else { + return param_env; + }; + + if new_predicates.is_empty() { + return param_env; + } + + let bounds = + tcx.mk_predicates(param_env.caller_bounds().iter().chain(new_predicates.iter().cloned())); + // FIXME(compiler-errors): Perhaps there is a case where we need to normalize this + // i.e. traits::normalize_param_env_or_error + ty::ParamEnv::new(bounds, param_env.reveal(), param_env.constness()) +} + +/// We use the following trait as an example throughout this function. +/// Specifically, let's assume that `to_check` here is the return type +/// of `into_iter`, and the GAT we are checking this for is `Iter`. +/// ```rust,ignore (this code fails due to this lint) +/// trait IntoIter { +/// type Iter<'a>: Iterator>; +/// type Item<'a>; +/// fn into_iter<'a>(&'a self) -> Self::Iter<'a>; +/// } +/// ``` +fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + item_hir: hir::HirId, + to_check: T, + wf_tys: &FxHashSet>, + gat_def_id: LocalDefId, + gat_generics: &'tcx ty::Generics, +) -> Option>> { + // The bounds we that we would require from `to_check` + let mut bounds = FxHashSet::default(); + + let (regions, types) = GATSubstCollector::visit(tcx, gat_def_id.to_def_id(), to_check); + + // If both regions and types are empty, then this GAT isn't in the + // set of types we are checking, and we shouldn't try to do clause analysis + // (particularly, doing so would end up with an empty set of clauses, + // since the current method would require none, and we take the + // intersection of requirements of all methods) + if types.is_empty() && regions.is_empty() { + return None; + } + + for (region_a, region_a_idx) in ®ions { + // Ignore `'static` lifetimes for the purpose of this lint: it's + // because we know it outlives everything and so doesn't give meaninful + // clues + if let ty::ReStatic = **region_a { + continue; + } + // For each region argument (e.g., `'a` in our example), check for a + // relationship to the type arguments (e.g., `Self`). If there is an + // outlives relationship (`Self: 'a`), then we want to ensure that is + // reflected in a where clause on the GAT itself. + for (ty, ty_idx) in &types { + // In our example, requires that `Self: 'a` + if ty_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *ty, *region_a) { + debug!(?ty_idx, ?region_a_idx); + debug!("required clause: {} must outlive {}", ty, region_a); + // Translate into the generic parameters of the GAT. In + // our example, the type was `Self`, which will also be + // `Self` in the GAT. + let ty_param = gat_generics.param_at(*ty_idx, tcx); + let ty_param = tcx + .mk_ty(ty::Param(ty::ParamTy { index: ty_param.index, name: ty_param.name })); + // Same for the region. In our example, 'a corresponds + // to the 'me parameter. + let region_param = gat_generics.param_at(*region_a_idx, tcx); + let region_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_param.def_id, + index: region_param.index, + name: region_param.name, + })); + // The predicate we expect to see. (In our example, + // `Self: 'me`.) + let clause = + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_param, region_param)); + let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); + bounds.insert(clause); + } + } + + // For each region argument (e.g., `'a` in our example), also check for a + // relationship to the other region arguments. If there is an outlives + // relationship, then we want to ensure that is reflected in the where clause + // on the GAT itself. + for (region_b, region_b_idx) in ®ions { + // Again, skip `'static` because it outlives everything. Also, we trivially + // know that a region outlives itself. + if ty::ReStatic == **region_b || region_a == region_b { + continue; + } + if region_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *region_a, *region_b) { + debug!(?region_a_idx, ?region_b_idx); + debug!("required clause: {} must outlive {}", region_a, region_b); + // Translate into the generic parameters of the GAT. + let region_a_param = gat_generics.param_at(*region_a_idx, tcx); + let region_a_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_a_param.def_id, + index: region_a_param.index, + name: region_a_param.name, + })); + // Same for the region. + let region_b_param = gat_generics.param_at(*region_b_idx, tcx); + let region_b_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_b_param.def_id, + index: region_b_param.index, + name: region_b_param.name, + })); + // The predicate we expect to see. + let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( + region_a_param, + region_b_param, + )); + let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); + bounds.insert(clause); + } + } + } + + Some(bounds) +} /// Given a known `param_env` and a set of well formed types, can we prove that /// `ty` outlives `region`. @@ -514,47 +600,21 @@ fn ty_known_to_outlive<'tcx>( ty: Ty<'tcx>, region: ty::Region<'tcx>, ) -> bool { - // Unfortunately, we have to use a new `InferCtxt` each call, because - // region constraints get added and solved there and we need to test each - // call individually. - tcx.infer_ctxt().enter(|infcx| { - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); - outlives_environment.save_implied_bounds(id); - let region_bound_pairs = outlives_environment.region_bound_pairs_map().get(&id).unwrap(); - - let cause = ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); - - let sup_type = ty; - let sub_region = region; - - let origin = SubregionOrigin::from_obligation_cause(&cause, || { - infer::RelateParamBound(cause.span, sup_type, None) - }); - + resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| { + let origin = infer::RelateParamBound(DUMMY_SP, ty, None); let outlives = &mut TypeOutlives::new( - &infcx, + infcx, tcx, - ®ion_bound_pairs, + region_bound_pairs, Some(infcx.tcx.lifetimes.re_root_empty), param_env, ); - outlives.type_must_outlive(origin, sup_type, sub_region); - - let errors = infcx.resolve_regions( - id.expect_owner().to_def_id(), - &outlives_environment, - RegionckMode::default(), - ); - - debug!(?errors, "errors"); - - // If we were able to prove that the type outlives the region without - // an error, it must be because of the implied or explicit bounds... - errors.is_empty() + outlives.type_must_outlive(origin, ty, region); }) } +/// Given a known `param_env` and a set of well formed types, can we prove that +/// `region_a` outlives `region_b` fn region_known_to_outlive<'tcx>( tcx: TyCtxt<'tcx>, id: hir::HirId, @@ -562,6 +622,27 @@ fn region_known_to_outlive<'tcx>( wf_tys: &FxHashSet>, region_a: ty::Region<'tcx>, region_b: ty::Region<'tcx>, +) -> bool { + resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |mut infcx, _| { + use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; + let origin = infer::RelateRegionParamBound(DUMMY_SP); + // `region_a: region_b` -> `region_b <= region_a` + infcx.push_sub_region_constraint(origin, region_b, region_a); + }) +} + +/// Given a known `param_env` and a set of well formed types, set up an +/// `InferCtxt`, call the passed function (to e.g. set up region constraints +/// to be tested), then resolve region and return errors +fn resolve_regions_with_wf_tys<'tcx>( + tcx: TyCtxt<'tcx>, + id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxHashSet>, + add_constraints: impl for<'a> FnOnce( + &'a InferCtxt<'a, 'tcx>, + &'a Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, + ), ) -> bool { // Unfortunately, we have to use a new `InferCtxt` each call, because // region constraints get added and solved there and we need to test each @@ -570,16 +651,9 @@ fn region_known_to_outlive<'tcx>( let mut outlives_environment = OutlivesEnvironment::new(param_env); outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); outlives_environment.save_implied_bounds(id); + let region_bound_pairs = outlives_environment.region_bound_pairs_map().get(&id).unwrap(); - let cause = ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); - - let origin = SubregionOrigin::from_obligation_cause(&cause, || { - infer::RelateRegionParamBound(cause.span) - }); - - use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; - // `region_a: region_b` -> `region_b <= region_a` - (&infcx).push_sub_region_constraint(origin, region_b, region_a); + add_constraints(&infcx, region_bound_pairs); let errors = infcx.resolve_regions( id.expect_owner().to_def_id(), @@ -628,6 +702,13 @@ impl<'tcx> GATSubstCollector<'tcx> { impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { type BreakTy = !; + fn visit_binder>( + &mut self, + t: &ty::Binder<'tcx, T>, + ) -> ControlFlow { + self.tcx.liberate_late_bound_regions(self.gat, t.clone()).visit_with(self) + } + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match t.kind() { ty::Projection(p) if p.item_def_id == self.gat => { @@ -647,10 +728,6 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { } t.super_visit_with(self) } - - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } } fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { @@ -666,13 +743,14 @@ fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { /// Detect when an object unsafe trait is referring to itself in one of its associated items. /// When this is done, suggest using `Self` instead. fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) { - let (trait_name, trait_def_id) = match tcx.hir().get(tcx.hir().get_parent_item(item.hir_id())) { - hir::Node::Item(item) => match item.kind { - hir::ItemKind::Trait(..) => (item.ident, item.def_id), + let (trait_name, trait_def_id) = + match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id())) { + hir::Node::Item(item) => match item.kind { + hir::ItemKind::Trait(..) => (item.ident, item.def_id), + _ => return, + }, _ => return, - }, - _ => return, - }; + }; let mut trait_should_be_self = vec![]; match &item.kind { hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty)) @@ -784,9 +862,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { } }; - if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty) - .is_some() - { + if traits::search_for_structural_match_violation(param.span, tcx, ty).is_some() { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it // clearer. @@ -860,7 +936,7 @@ fn check_associated_item( let hir_sig = sig_if_method.expect("bad signature for method"); check_fn_or_method( fcx, - item.ident.span, + item.ident(fcx.tcx).span, sig, hir_sig.decl, item.def_id, @@ -1023,6 +1099,11 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { FxHashSet::default() }); + + // Only check traits, don't check trait aliases + if let hir::ItemKind::Trait(_, _, _, _, items) = item.kind { + check_gat_where_clauses(tcx, items); + } } /// Checks all associated type defaults of trait `trait_def_id`. @@ -1197,7 +1278,7 @@ fn check_where_clauses<'tcx, 'fcx>( // Ignore dependent defaults -- that is, where the default of one type // parameter includes another (e.g., ``). In those cases, we can't // be sure if it will error or not as user might always specify the other. - if !ty.definitely_needs_subst(tcx) { + if !ty.needs_subst() { fcx.register_wf_obligation( ty.into(), tcx.def_span(param.def_id), @@ -1213,7 +1294,7 @@ fn check_where_clauses<'tcx, 'fcx>( // for `struct Foo` // we should eagerly error. let default_ct = tcx.const_param_default(param.def_id); - if !default_ct.definitely_needs_subst(tcx) { + if !default_ct.needs_subst() { fcx.register_wf_obligation( default_ct.into(), tcx.def_span(param.def_id), @@ -1247,7 +1328,7 @@ fn check_where_clauses<'tcx, 'fcx>( if is_our_default(param) { let default_ty = tcx.type_of(param.def_id); // ... and it's not a dependent default, ... - if !default_ty.definitely_needs_subst(tcx) { + if !default_ty.needs_subst() { // ... then substitute it with the default. return default_ty.into(); } @@ -1260,7 +1341,7 @@ fn check_where_clauses<'tcx, 'fcx>( if is_our_default(param) { let default_ct = tcx.const_param_default(param.def_id); // ... and it's not a dependent default, ... - if !default_ct.definitely_needs_subst(tcx) { + if !default_ct.needs_subst() { // ... then substitute it with the default. return default_ct.into(); } @@ -1276,15 +1357,12 @@ fn check_where_clauses<'tcx, 'fcx>( .predicates .iter() .flat_map(|&(pred, sp)| { - struct CountParams<'tcx> { - tcx: TyCtxt<'tcx>, + #[derive(Default)] + struct CountParams { params: FxHashSet, } - impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams<'tcx> { + impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams { type BreakTy = (); - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { if let ty::Param(param) = t.kind() { @@ -1297,19 +1375,19 @@ fn check_where_clauses<'tcx, 'fcx>( ControlFlow::BREAK } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { - if let ty::ConstKind::Param(param) = c.val { + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + if let ty::ConstKind::Param(param) = c.val() { self.params.insert(param.index); } c.super_visit_with(self) } } - let mut param_count = CountParams { tcx: fcx.tcx, params: FxHashSet::default() }; + let mut param_count = CountParams::default(); let has_region = pred.visit_with(&mut param_count).is_break(); let substituted_pred = pred.subst(tcx, substs); // Don't check non-defaulted params, dependent defaults (including lifetimes) // or preds with multiple params. - if substituted_pred.definitely_has_param_types_or_consts(tcx) + if substituted_pred.has_param_types_or_consts() || param_count.params.len() > 1 || has_region { @@ -1697,7 +1775,7 @@ fn check_false_global_bounds(fcx: &FnCtxt<'_, '_>, mut span: Span, id: hir::HirI for obligation in implied_obligations { let pred = obligation.predicate; // Match the existing behavior. - if pred.is_global(fcx.tcx) && !pred.has_late_bound_regions() { + if pred.is_global() && !pred.has_late_bound_regions() { let pred = fcx.normalize_associated_types_in(span, pred); let hir_node = fcx.tcx.hir().find(id); @@ -1759,10 +1837,10 @@ impl<'tcx> ParItemLikeVisitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for CheckTypeWellFormedVisitor<'tcx> { - type Map = hir_map::Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { - hir_visit::NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } #[instrument(skip(self, i), level = "debug")] diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index fdc8b6b5e6..3843e7e54b 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -8,7 +8,7 @@ use rustc_data_structures::stable_map::FxHashMap; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::InferCtxt; use rustc_middle::hir::place::Place as HirPlace; @@ -43,7 +43,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let item_def_id = self.tcx.hir().local_def_id(item_id); // This attribute causes us to dump some writeback information - // in the form of errors, which is uSymbol for unit tests. + // in the form of errors, which is used for unit tests. let rustc_dump_user_substs = self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs); @@ -130,7 +130,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn write_ty_to_typeck_results(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) { debug!("write_ty_to_typeck_results({:?}, {:?})", hir_id, ty); - assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions(self.tcx())); + assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions()); self.typeck_results.node_types_mut().insert(hir_id, ty); } @@ -253,12 +253,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // traffic in node-ids or update typeck results in the type context etc. impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { self.fix_scalar_builtin_expr(e); self.fix_index_builtin_expr(e); @@ -726,7 +720,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { } } - fn report_const_error(&self, c: &'tcx ty::Const<'tcx>) { + fn report_const_error(&self, c: ty::Const<'tcx>) { if !self.tcx.sess.has_errors() { self.infcx .emit_inference_failure_err( @@ -750,14 +744,14 @@ impl<'tcx> TypeFolder<'tcx> for EraseEarlyRegions<'tcx> { self.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_type_flags(ty::TypeFlags::HAS_POTENTIAL_FREE_REGIONS) { + if ty.has_type_flags(ty::TypeFlags::HAS_FREE_REGIONS) { ty.super_fold_with(self) } else { ty } } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReLateBound(..) = r { r } else { self.tcx.lifetimes.re_erased } + if r.is_late_bound() { r } else { self.tcx.lifetimes.re_erased } } } @@ -789,14 +783,14 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { self.tcx.lifetimes.re_erased } - fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match self.infcx.fully_resolve(ct) { Ok(ct) => self.infcx.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); self.report_const_error(ct); self.replaced_with_error = true; - self.tcx().const_error(ct.ty) + self.tcx().const_error(ct.ty()) } } } diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index d5494c5a68..401ba18872 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -147,7 +147,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: use ty::TyKind::*; match (source.kind(), target.kind()) { (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) - if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {} + if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {} (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) if def_a.is_struct() && def_b.is_struct() => @@ -199,7 +199,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: ) .note(&format!( "extra field `{}` of type `{}` is not allowed", - field.ident, ty_a, + field.name, ty_a, )) .emit(); @@ -235,7 +235,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: .map(|field| { format!( "`{}` (`{}` to `{}`)", - field.ident, + field.name, field.ty(tcx, substs_a), field.ty(tcx, substs_b), ) @@ -479,7 +479,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn diff_fields .iter() .map(|&(i, a, b)| { - format!("`{}` (`{}` to `{}`)", fields[i].ident, a, b) + format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) }) .collect::>() .join(", ") diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index 59f211bd2c..cf71e0f300 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_index::vec::IndexVec; +use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; use rustc_trait_selection::traits::{self, SkipLeakCheck}; @@ -36,7 +37,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { for item1 in impl_items1.in_definition_order() { let collision = impl_items2 - .filter_by_name_unhygienic(item1.ident.name) + .filter_by_name_unhygienic(item1.name) .any(|item2| self.compare_hygienically(item1, item2)); if collision { @@ -50,7 +51,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> { fn compare_hygienically(&self, item1: &ty::AssocItem, item2: &ty::AssocItem) -> bool { // Symbols and namespace match, compare hygienically. item1.kind.namespace() == item2.kind.namespace() - && item1.ident.normalize_to_macros_2_0() == item2.ident.normalize_to_macros_2_0() + && item1.ident(self.tcx).normalize_to_macros_2_0() + == item2.ident(self.tcx).normalize_to_macros_2_0() } fn check_for_common_items_in_impls( @@ -64,11 +66,11 @@ impl<'tcx> InherentOverlapChecker<'tcx> { for item1 in impl_items1.in_definition_order() { let collision = impl_items2 - .filter_by_name_unhygienic(item1.ident.name) + .filter_by_name_unhygienic(item1.name) .find(|item2| self.compare_hygienically(item1, item2)); if let Some(item2) = collision { - let name = item1.ident.normalize_to_macros_2_0(); + let name = item1.ident(self.tcx).normalize_to_macros_2_0(); let mut err = struct_span_err!( self.tcx.sess, self.tcx.span_of_impl(item1.def_id).unwrap(), @@ -98,7 +100,12 @@ impl<'tcx> InherentOverlapChecker<'tcx> { } } - fn check_for_overlapping_inherent_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId) { + fn check_for_overlapping_inherent_impls( + &self, + overlap_mode: OverlapMode, + impl1_def_id: DefId, + impl2_def_id: DefId, + ) { traits::overlapping_impls( self.tcx, impl1_def_id, @@ -106,6 +113,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> { // We go ahead and just skip the leak check for // inherent impls without warning. SkipLeakCheck::Yes, + overlap_mode, |overlap| { self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap); false @@ -130,6 +138,8 @@ impl<'tcx> ItemLikeVisitor<'_> for InherentOverlapChecker<'tcx> { return; } + let overlap_mode = OverlapMode::get(self.tcx, item.def_id.to_def_id()); + let impls_items = impls .iter() .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id))) @@ -144,6 +154,7 @@ impl<'tcx> ItemLikeVisitor<'_> for InherentOverlapChecker<'tcx> { for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { if self.impls_have_common_items(impl_items1, impl_items2) { self.check_for_overlapping_inherent_impls( + overlap_mode, impl1_def_id, impl2_def_id, ); @@ -181,11 +192,11 @@ impl<'tcx> ItemLikeVisitor<'_> for InherentOverlapChecker<'tcx> { let mut ids = impl_items .in_definition_order() .filter_map(|item| { - let entry = connected_region_ids.entry(item.ident.name); + let entry = connected_region_ids.entry(item.name); if let Entry::Occupied(e) = &entry { Some(*e.get()) } else { - idents_to_add.push(item.ident.name); + idents_to_add.push(item.name); None } }) @@ -287,6 +298,7 @@ impl<'tcx> ItemLikeVisitor<'_> for InherentOverlapChecker<'tcx> { let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; if self.impls_have_common_items(impl_items1, impl_items2) { self.check_for_overlapping_inherent_impls( + overlap_mode, impl1_def_id, impl2_def_id, ); diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs index 377ebf1fe2..055818f55f 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_typeck/src/coherence/mod.rs @@ -121,28 +121,6 @@ fn enforce_trait_manually_implementable( return; } } - - let trait_name = if did == li.fn_trait() { - "Fn" - } else if did == li.fn_mut_trait() { - "FnMut" - } else if did == li.fn_once_trait() { - "FnOnce" - } else { - return; // everything OK - }; - - let span = impl_header_span(tcx, impl_def_id); - struct_span_err!( - tcx.sess, - span, - E0183, - "manual implementations of `{}` are experimental", - trait_name - ) - .span_label(span, format!("manual implementations of `{}` are experimental", trait_name)) - .help("add `#![feature(unboxed_closures)]` to the crate attributes to enable") - .emit(); } /// We allow impls of marker traits to overlap, so they can't override impls diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index e954b4cf51..20f42b16b1 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -1,24 +1,33 @@ //! Orphan checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. +use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_errors::ErrorReported; use rustc_hir as hir; +use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::TyCtxtInferExt; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::def_id::LocalDefId; +use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; +use rustc_middle::ty::{self, ImplPolarity, Ty, TyCtxt, TypeFoldable, TypeVisitor}; +use rustc_session::lint; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; use rustc_trait_selection::traits; +use std::ops::ControlFlow; pub(super) fn orphan_check_crate(tcx: TyCtxt<'_>, (): ()) -> &[LocalDefId] { let mut errors = Vec::new(); - for (_trait, impls_of_trait) in tcx.all_local_trait_impls(()) { + for (&trait_def_id, impls_of_trait) in tcx.all_local_trait_impls(()) { for &impl_of_trait in impls_of_trait { match orphan_check_impl(tcx, impl_of_trait) { Ok(()) => {} Err(ErrorReported) => errors.push(impl_of_trait), } } + + if tcx.trait_is_auto(trait_def_id) { + lint_auto_trait_impls(tcx, trait_def_id, impls_of_trait); + } } tcx.arena.alloc_slice(&errors) } @@ -265,3 +274,196 @@ fn emit_orphan_check_error<'tcx>( Err(ErrorReported) } + +#[derive(Default)] +struct AreUniqueParamsVisitor { + seen: GrowableBitSet, +} + +#[derive(Copy, Clone)] +enum NotUniqueParam<'tcx> { + DuplicateParam(GenericArg<'tcx>), + NotParam(GenericArg<'tcx>), +} + +impl<'tcx> TypeVisitor<'tcx> for AreUniqueParamsVisitor { + type BreakTy = NotUniqueParam<'tcx>; + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + match t.kind() { + ty::Param(p) => { + if self.seen.insert(p.index) { + ControlFlow::CONTINUE + } else { + ControlFlow::Break(NotUniqueParam::DuplicateParam(t.into())) + } + } + _ => ControlFlow::Break(NotUniqueParam::NotParam(t.into())), + } + } + fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow { + // We don't drop candidates during candidate assembly because of region + // constraints, so the behavior for impls only constrained by regions + // will not change. + ControlFlow::CONTINUE + } + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + match c.val() { + ty::ConstKind::Param(p) => { + if self.seen.insert(p.index) { + ControlFlow::CONTINUE + } else { + ControlFlow::Break(NotUniqueParam::DuplicateParam(c.into())) + } + } + _ => ControlFlow::Break(NotUniqueParam::NotParam(c.into())), + } + } +} + +/// Lint impls of auto traits if they are likely to have +/// unsound or surprising effects on auto impls. +fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDefId]) { + let mut non_covering_impls = Vec::new(); + for &impl_def_id in impls { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + if trait_ref.references_error() { + return; + } + + if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive { + return; + } + + assert_eq!(trait_ref.substs.len(), 1); + let self_ty = trait_ref.self_ty(); + let (self_type_did, substs) = match self_ty.kind() { + ty::Adt(def, substs) => (def.did, substs), + _ => { + // FIXME: should also lint for stuff like `&i32` but + // considering that auto traits are unstable, that + // isn't too important for now as this only affects + // crates using `nightly`, and std. + continue; + } + }; + + // Impls which completely cover a given root type are fine as they + // disable auto impls entirely. So only lint if the substs + // are not a permutation of the identity substs. + match substs.visit_with(&mut AreUniqueParamsVisitor::default()) { + ControlFlow::Continue(()) => {} // ok + ControlFlow::Break(arg) => { + // Ideally: + // + // - compute the requirements for the auto impl candidate + // - check whether these are implied by the non covering impls + // - if not, emit the lint + // + // What we do here is a bit simpler: + // + // - badly check if an auto impl candidate definitely does not apply + // for the given simplified type + // - if so, do not lint + if fast_reject_auto_impl(tcx, trait_def_id, self_ty) { + // ok + } else { + non_covering_impls.push((impl_def_id, self_type_did, arg)); + } + } + } + } + + for &(impl_def_id, self_type_did, arg) in &non_covering_impls { + tcx.struct_span_lint_hir( + lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, + tcx.hir().local_def_id_to_hir_id(impl_def_id), + tcx.def_span(impl_def_id), + |err| { + let mut err = err.build(&format!( + "cross-crate traits with a default impl, like `{}`, \ + should not be specialized", + tcx.def_path_str(trait_def_id), + )); + let item_span = tcx.def_span(self_type_did); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); + err.span_note( + item_span, + &format!( + "try using the same sequence of generic parameters as the {} definition", + self_descr, + ), + ); + match arg { + NotUniqueParam::DuplicateParam(arg) => { + err.note(&format!("`{}` is mentioned multiple times", arg)); + } + NotUniqueParam::NotParam(arg) => { + err.note(&format!("`{}` is not a generic parameter", arg)); + } + } + err.emit(); + }, + ); + } +} + +fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool { + struct DisableAutoTraitVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + self_ty_root: Ty<'tcx>, + seen: FxHashSet, + } + + impl<'tcx> TypeVisitor<'tcx> for DisableAutoTraitVisitor<'tcx> { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + let tcx = self.tcx; + if t != self.self_ty_root { + for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) { + match tcx.impl_polarity(impl_def_id) { + ImplPolarity::Negative => return ControlFlow::BREAK, + ImplPolarity::Reservation => {} + // FIXME(@lcnr): That's probably not good enough, idk + // + // We might just want to take the rustdoc code and somehow avoid + // explicit impls for `Self`. + ImplPolarity::Positive => return ControlFlow::CONTINUE, + } + } + } + + match t.kind() { + ty::Adt(def, substs) if def.is_phantom_data() => substs.super_visit_with(self), + ty::Adt(def, substs) => { + // @lcnr: This is the only place where cycles can happen. We avoid this + // by only visiting each `DefId` once. + // + // This will be is incorrect in subtle cases, but I don't care :) + if self.seen.insert(def.did) { + for ty in def.all_fields().map(|field| field.ty(tcx, substs)) { + ty.visit_with(self)?; + } + } + + ControlFlow::CONTINUE + } + _ => t.super_visit_with(self), + } + } + } + + let self_ty_root = match self_ty.kind() { + ty::Adt(def, _) => tcx.mk_adt(def, InternalSubsts::identity_for_item(tcx, def.did)), + _ => unimplemented!("unexpected self ty {:?}", self_ty), + }; + + self_ty_root + .visit_with(&mut DisableAutoTraitVisitor { + tcx, + self_ty_root, + trait_def_id, + seen: FxHashSet::default(), + }) + .is_break() +} diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 41c8a37a71..18f54eb224 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! "Collection" is the process of determining the type and other external //! details of each item in Rust. Collection is specifically concerned //! with *inter-procedural* things -- for example, for a function @@ -21,7 +22,6 @@ use crate::constrained_generic_params as cgp; use crate::errors; use crate::middle::resolve_lifetime as rl; use rustc_ast as ast; -use rustc_ast::Attribute; use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::captures::Captures; @@ -29,11 +29,11 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::weak_lang_items; use rustc_hir::{GenericParamKind, HirId, Node}; -use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::mono::Linkage; use rustc_middle::ty::query::Providers; @@ -68,7 +68,6 @@ fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { pub fn provide(providers: &mut Providers) { *providers = Providers { opt_const_param_of: type_of::opt_const_param_of, - default_anon_const_substs: type_of::default_anon_const_substs, type_of: type_of::type_of, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, @@ -114,14 +113,9 @@ pub struct ItemCtxt<'tcx> { /////////////////////////////////////////////////////////////////////////// #[derive(Default)] -crate struct PlaceholderHirTyCollector(crate Vec); +crate struct HirPlaceholderCollector(crate Vec); -impl<'v> Visitor<'v> for PlaceholderHirTyCollector { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } +impl<'v> Visitor<'v> for HirPlaceholderCollector { fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { if let hir::TyKind::Infer = t.kind { self.0.push(t.span); @@ -138,6 +132,12 @@ impl<'v> Visitor<'v> for PlaceholderHirTyCollector { _ => {} } } + fn visit_array_length(&mut self, length: &'v hir::ArrayLen) { + if let &hir::ArrayLen::Infer(_, span) = length { + self.0.push(span); + } + intravisit::walk_array_len(self, length) + } } struct CollectItemTypesVisitor<'tcx> { @@ -182,7 +182,7 @@ crate fn placeholder_type_error<'tcx>( sugg.push((span, format!(", {}", type_name))); } - let mut err = bad_placeholder(tcx, "type", placeholder_types, kind); + let mut err = bad_placeholder(tcx, placeholder_types, kind); // Suggest, but only if it is not a function in const or static if suggest { @@ -240,7 +240,7 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( _ => return, }; - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); visitor.visit_item(item); placeholder_type_error( @@ -255,10 +255,10 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( } impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { @@ -318,7 +318,6 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { fn bad_placeholder<'tcx>( tcx: TyCtxt<'tcx>, - placeholder_kind: &'static str, mut spans: Vec, kind: &'static str, ) -> rustc_errors::DiagnosticBuilder<'tcx> { @@ -329,8 +328,7 @@ fn bad_placeholder<'tcx>( tcx.sess, spans.clone(), E0121, - "the {} placeholder `_` is not allowed within types on item signatures for {}", - placeholder_kind, + "the placeholder `_` is not allowed within types on item signatures for {}", kind ); for span in spans { @@ -388,22 +386,15 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { } fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - self.tcx().ty_error_with_message(span, "bad_placeholder_type") + self.tcx().ty_error_with_message(span, "bad placeholder type") } - fn ct_infer( - &self, - ty: Ty<'tcx>, - _: Option<&ty::GenericParamDef>, - span: Span, - ) -> &'tcx Const<'tcx> { - bad_placeholder(self.tcx(), "const", vec![span], "generic").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 { + fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> { + 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) + self.tcx().const_error_with_message(ty, span, "bad placeholder constant") } fn projected_ty_from_poly_trait_ref( @@ -436,7 +427,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { match self.node() { hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => { let item = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_did(self.hir_id())); + self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(self.hir_id())); match &item.kind { hir::ItemKind::Enum(_, generics) | hir::ItemKind::Struct(_, generics) @@ -570,13 +561,12 @@ fn type_param_predicates( let param_id = tcx.hir().local_def_id_to_hir_id(def_id); let param_owner = tcx.hir().ty_param_owner(param_id); - let param_owner_def_id = tcx.hir().local_def_id(param_owner); - let generics = tcx.generics_of(param_owner_def_id); + let generics = tcx.generics_of(param_owner); let index = generics.param_def_id_to_index[&def_id.to_def_id()]; let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(param_id)); // Don't look for bounds where the type parameter isn't in scope. - let parent = if item_def_id == param_owner_def_id.to_def_id() { + let parent = if item_def_id == param_owner.to_def_id() { None } else { tcx.generics_of(item_def_id).parent @@ -667,7 +657,11 @@ impl<'tcx> ItemCtxt<'tcx> { .params .iter() .filter_map(|param| match param.kind { - GenericParamKind::Type { .. } if param.hir_id == param_id => Some(¶m.bounds), + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } + if param.hir_id == param_id => + { + Some(¶m.bounds) + } _ => None, }) .flat_map(|bounds| bounds.iter()) @@ -747,7 +741,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { match item.kind { hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.def_id), hir::ForeignItemKind::Static(..) => { - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); visitor.visit_foreign_item(item); placeholder_type_error( tcx, @@ -830,7 +824,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { hir::ItemKind::Const(ty, ..) | hir::ItemKind::Static(ty, ..) => { // (#75889): Account for `const C: dyn Fn() -> _ = "";` if let hir::TyKind::TraitObject(..) = ty.kind { - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); visitor.visit_item(it); placeholder_type_error( tcx, @@ -866,7 +860,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Const(..) => { tcx.ensure().type_of(trait_item_id.def_id); // Account for `const C: _;`. - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); placeholder_type_error(tcx, None, &[], visitor.0, false, None, "constant"); } @@ -875,7 +869,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { tcx.ensure().item_bounds(trait_item_id.def_id); tcx.ensure().type_of(trait_item_id.def_id); // Account for `type T = _;`. - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type"); } @@ -884,7 +878,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { tcx.ensure().item_bounds(trait_item_id.def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type"); @@ -906,7 +900,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { } hir::ImplItemKind::TyAlias(_) => { // Account for `type T = _;` - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); visitor.visit_impl_item(impl_item); placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type"); @@ -995,7 +989,7 @@ fn convert_variant( seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); } - ty::FieldDef { did: fid.to_def_id(), ident: f.ident, vis: tcx.visibility(fid) } + ty::FieldDef { did: fid.to_def_id(), name: f.ident.name, vis: tcx.visibility(fid) } }) .collect(); let recovered = match def { @@ -1003,7 +997,7 @@ fn convert_variant( _ => false, }; ty::VariantDef::new( - ident, + ident.name, variant_did.map(LocalDefId::to_def_id), ctor_did.map(LocalDefId::to_def_id), discr, @@ -1198,9 +1192,11 @@ fn super_predicates_that_define_assoc_type( fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { let item = tcx.hir().expect_item(def_id.expect_local()); - let (is_auto, unsafety) = match item.kind { - hir::ItemKind::Trait(is_auto, unsafety, ..) => (is_auto == hir::IsAuto::Yes, unsafety), - hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal), + let (is_auto, unsafety, items) = match item.kind { + hir::ItemKind::Trait(is_auto, unsafety, .., items) => { + (is_auto == hir::IsAuto::Yes, unsafety, items) + } + hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal, &[][..]), _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"), }; @@ -1227,6 +1223,103 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { ty::trait_def::TraitSpecializationKind::None }; let def_path_hash = tcx.def_path_hash(def_id); + + let must_implement_one_of = tcx + .get_attrs(def_id) + .iter() + .find(|attr| attr.has_name(sym::rustc_must_implement_one_of)) + // Check that there are at least 2 arguments of `#[rustc_must_implement_one_of]` + // and that they are all identifiers + .and_then(|attr| match attr.meta_item_list() { + Some(items) if items.len() < 2 => { + tcx.sess + .struct_span_err( + attr.span, + "the `#[rustc_must_implement_one_of]` attribute must be \ + used with at least 2 args", + ) + .emit(); + + None + } + Some(items) => items + .into_iter() + .map(|item| item.ident().ok_or(item.span())) + .collect::, _>>() + .map_err(|span| { + tcx.sess + .struct_span_err(span, "must be a name of an associated function") + .emit(); + }) + .ok() + .zip(Some(attr.span)), + // Error is reported by `rustc_attr!` + None => None, + }) + // Check that all arguments of `#[rustc_must_implement_one_of]` reference + // functions in the trait with default implementations + .and_then(|(list, attr_span)| { + let errors = list.iter().filter_map(|ident| { + let item = items.iter().find(|item| item.ident == *ident); + + match item { + Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => { + if !item.defaultness.has_value() { + tcx.sess + .struct_span_err( + item.span, + "This function doesn't have a default implementation", + ) + .span_note(attr_span, "required by this annotation") + .emit(); + + return Some(()); + } + + return None; + } + Some(item) => tcx + .sess + .struct_span_err(item.span, "Not a function") + .span_note(attr_span, "required by this annotation") + .note( + "All `#[rustc_must_implement_one_of]` arguments \ + must be associated function names", + ) + .emit(), + None => tcx + .sess + .struct_span_err(ident.span, "Function not found in this trait") + .emit(), + } + + Some(()) + }); + + (errors.count() == 0).then_some(list) + }) + // Check for duplicates + .and_then(|list| { + let mut set: FxHashMap = FxHashMap::default(); + let mut no_dups = true; + + for ident in &*list { + if let Some(dup) = set.insert(ident.name, ident.span) { + tcx.sess + .struct_span_err(vec![dup, ident.span], "Functions names are duplicated") + .note( + "All `#[rustc_must_implement_one_of]` arguments \ + must be unique", + ) + .emit(); + + no_dups = false; + } + } + + no_dups.then_some(list) + }); + ty::TraitDef::new( def_id, unsafety, @@ -1236,6 +1329,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { skip_array_during_method_dispatch, spec_kind, def_path_hash, + must_implement_one_of, ) } @@ -1247,12 +1341,6 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { if self.has_late_bound_regions.is_some() { return; @@ -1360,12 +1448,6 @@ struct AnonConstInParamTyDetector { } impl<'v> Visitor<'v> for AnonConstInParamTyDetector { - type Map = intravisit::ErasedMap<'v>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) { if let GenericParamKind::Const { ty, default: _ } = p.kind { let prev = self.in_param_ty; @@ -1397,13 +1479,12 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { | Node::Ctor(..) | Node::Field(_) => { let parent_id = tcx.hir().get_parent_item(hir_id); - Some(tcx.hir().local_def_id(parent_id).to_def_id()) + Some(parent_id.to_def_id()) } // FIXME(#43408) always enable this once `lazy_normalization` is // stable enough and does not need a feature gate anymore. Node::AnonConst(_) => { - let parent_id = tcx.hir().get_parent_item(hir_id); - let parent_def_id = tcx.hir().local_def_id(parent_id); + let parent_def_id = tcx.hir().get_parent_item(hir_id); let mut in_param_ty = false; for (_parent, node) in tcx.hir().parent_iter(hir_id) { @@ -1513,11 +1594,11 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { }) => Some(fn_def_id.to_def_id()), ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { let parent_id = tcx.hir().get_parent_item(hir_id); - assert!(parent_id != hir_id && parent_id != CRATE_HIR_ID); + assert_ne!(parent_id, CRATE_DEF_ID); debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); // Opaque types are always nested within another item, and // inherit the generics of the item. - Some(tcx.hir().local_def_id(parent_id).to_def_id()) + Some(parent_id.to_def_id()) } _ => None, }, @@ -1610,7 +1691,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { kind: ty::GenericParamDefKind::Lifetime, })); - let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id); + let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id.owner); // Now create the real type and const parameters. let type_start = own_start - has_self as u32 + params.len() as u32; @@ -1739,10 +1820,14 @@ fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { /// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to /// use inference to provide suggestions for the appropriate type if possible. fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { + debug!(?ty); use hir::TyKind::*; match &ty.kind { Infer => true, - Slice(ty) | Array(ty, _) => is_suggestable_infer_ty(ty), + Slice(ty) => is_suggestable_infer_ty(ty), + Array(ty, length) => { + is_suggestable_infer_ty(ty) || matches!(length, hir::ArrayLen::Infer(_, _)) + } Tup(tys) => tys.iter().any(is_suggestable_infer_ty), Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty), OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args), @@ -1788,15 +1873,15 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { 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 { + let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r { ty::ReErased => tcx.lifetimes.re_static, _ => r, }); let fn_sig = ty::Binder::dummy(fn_sig); - let mut visitor = PlaceholderHirTyCollector::default(); + let mut visitor = HirPlaceholderCollector::default(); visitor.visit_ty(ty); - let mut diag = bad_placeholder(tcx, "type", visitor.0, "return type"); + let mut diag = bad_placeholder(tcx, visitor.0, "return type"); let ret_ty = fn_sig.skip_binder().output(); if !ret_ty.references_error() { if !ret_ty.is_closure() { @@ -1862,7 +1947,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { } Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => { - let ty = tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()); + let ty = tcx.type_of(tcx.hir().get_parent_item(hir_id)); let inputs = data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id))); ty::Binder::dummy(tcx.mk_fn_sig( @@ -2280,7 +2365,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP tcx, &mut predicates, trait_ref, - &mut cgp::parameters_for_impl(tcx, self_ty, trait_ref), + &mut cgp::parameters_for_impl(self_ty, trait_ref), ); } @@ -2302,16 +2387,10 @@ fn const_evaluatable_predicates_of<'tcx>( } impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> { - type Map = Map<'tcx>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { let def_id = self.tcx.hir().local_def_id(c.hir_id); let ct = ty::Const::from_anon_const(self.tcx, def_id); - if let ty::ConstKind::Unevaluated(uv) = ct.val { + if let ty::ConstKind::Unevaluated(uv) = ct.val() { assert_eq!(uv.promoted, None); let span = self.tcx.hir().span(c.hir_id); self.preds.insert(( @@ -2432,8 +2511,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat // parent of generics returned by `generics_of` // // In the above code we want the anon const to have predicates in its param env for `T: Trait` - let item_id = tcx.hir().get_parent_item(hir_id); - let item_def_id = tcx.hir().local_def_id(item_id).to_def_id(); + let item_def_id = tcx.hir().get_parent_item(hir_id); // In the above code example we would be calling `explicit_predicates_of(Foo)` here return tcx.explicit_predicates_of(item_def_id); } @@ -2455,7 +2533,7 @@ fn predicates_from_bound<'tcx>( ) -> Vec<(ty::Predicate<'tcx>, Span)> { let mut bounds = Bounds::default(); astconv.add_bounds(param_ty, [bound].into_iter(), &mut bounds, bound_vars); - bounds.predicates(astconv.tcx(), param_ty) + bounds.predicates(astconv.tcx(), param_ty).collect() } fn compute_sig_of_foreign_fn_decl<'tcx>( @@ -2509,7 +2587,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( } }; for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) { - check(input, ty) + check(input, *ty) } if let hir::FnRetTy::Return(ref ty) = decl.output { check(ty, fty.output().skip_binder()) @@ -2696,6 +2774,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } } + // The panic_no_unwind function called by TerminatorKind::Abort will never + // unwind. If the panic handler that it invokes unwind then it will simply + // call the panic handler again. + if Some(id) == tcx.lang_items().panic_no_unwind() { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; + } + let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); let mut inline_span = None; @@ -2767,7 +2852,42 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } else if attr.has_name(sym::rustc_std_internal_symbol) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } else if attr.has_name(sym::used) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; + let inner = attr.meta_item_list(); + match inner.as_deref() { + Some([item]) if item.has_name(sym::linker) => { + if !tcx.features().used_with_arg { + feature_err( + &tcx.sess.parse_sess, + sym::used_with_arg, + attr.span, + "`#[used(linker)]` is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER; + } + Some([item]) if item.has_name(sym::compiler) => { + if !tcx.features().used_with_arg { + feature_err( + &tcx.sess.parse_sess, + sym::used_with_arg, + attr.span, + "`#[used(compiler)]` is currently unstable", + ) + .emit(); + } + codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; + } + Some(_) => { + tcx.sess + .struct_span_err( + attr.span, + "expected `used`, `used(compiler)` or `used(linker)`", + ) + .emit(); + } + None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED, + } } else if attr.has_name(sym::cmse_nonsecure_entry) { if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) { struct_span_err!( @@ -2889,6 +3009,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI; } else if item.has_name(sym::memory) { codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; + } else if item.has_name(sym::memtag) { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG; } else if item.has_name(sym::thread) { codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; } else if item.has_name(sym::hwaddress) { @@ -2896,7 +3018,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } else { tcx.sess .struct_span_err(item.span(), "invalid argument for `no_sanitize`") - .note("expected one of: `address`, `hwaddress`, `memory` or `thread`") + .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, or `thread`") .emit(); } } @@ -3120,8 +3242,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { if tcx.is_weak_lang_item(id) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } - let check_name = |attr: &Attribute, sym| attr.has_name(sym); - if let Some(name) = weak_lang_items::link_name(check_name, attrs) { + if let Some(name) = weak_lang_items::link_name(attrs) { codegen_fn_attrs.export_name = Some(name); codegen_fn_attrs.link_name = Some(name); } @@ -3150,21 +3271,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { if let Some(impl_item) = tcx.opt_associated_item(def_id) { - if let ty::AssocItemContainer::ImplContainer(impl_def_id) = impl_item.container { - if let Some(trait_def_id) = tcx.trait_id_of_impl(impl_def_id) { - if let Some(trait_item) = tcx - .associated_items(trait_def_id) - .filter_by_name_unhygienic(impl_item.ident.name) - .find(move |trait_item| { - trait_item.kind == ty::AssocKind::Fn - && tcx.hygienic_eq(impl_item.ident, trait_item.ident, trait_def_id) - }) - { - return tcx - .codegen_fn_attrs(trait_item.def_id) - .flags - .intersects(CodegenFnAttrFlags::TRACK_CALLER); - } + if let ty::AssocItemContainer::ImplContainer(_) = impl_item.container { + if let Some(trait_item) = impl_item.trait_item_def_id { + return tcx + .codegen_fn_attrs(trait_item) + .flags + .intersects(CodegenFnAttrFlags::TRACK_CALLER); } } } @@ -3241,7 +3353,7 @@ fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: let hir_id = tcx.hir().local_def_id_to_hir_id(id); let node = tcx.hir().get(hir_id); if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { - let parent_id = tcx.hir().get_parent_did(hir_id); + let parent_id = tcx.hir().get_parent_item(hir_id); let parent_item = tcx.hir().expect_item(parent_id); if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind { tcx.sess diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_typeck/src/collect/item_bounds.rs index 26cad8fb18..87a67c4a4e 100644 --- a/compiler/rustc_typeck/src/collect/item_bounds.rs +++ b/compiler/rustc_typeck/src/collect/item_bounds.rs @@ -67,11 +67,7 @@ fn opaque_type_bounds<'tcx>( let mut bounds = >::compute_bounds(&icx, item_ty, ast_bounds); // Opaque types are implicitly sized unless a `?Sized` bound is found >::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span); - let bounds = bounds.predicates(tcx, item_ty); - - debug!("opaque_type_bounds({}) = {:?}", tcx.def_path_str(opaque_def_id), bounds); - - tcx.arena.alloc_slice(&bounds) + tcx.arena.alloc_from_iter(bounds.predicates(tcx, item_ty)) }) } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index ae8d262fcf..39da7c82c4 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -1,14 +1,14 @@ use rustc_errors::{Applicability, ErrorReported, StashKey}; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit; use rustc_hir::intravisit::Visitor; use rustc_hir::{HirId, Node}; -use rustc_middle::hir::map::Map; -use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder}; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; @@ -18,238 +18,218 @@ use super::{bad_placeholder, is_suggestable_infer_ty}; /// Computes the relevant generic parameter for a potential generic const argument. /// /// This should be called using the query `tcx.opt_const_param_of`. +#[instrument(level = "debug", skip(tcx))] pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - // FIXME(generic_arg_infer): allow for returning DefIds of inference of - // GenericArg::Infer below. This may require a change where GenericArg::Infer has some flag - // for const or type. use hir::*; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - if let Node::AnonConst(_) = tcx.hir().get(hir_id) { - let parent_node_id = tcx.hir().get_parent_node(hir_id); - let parent_node = tcx.hir().get(parent_node_id); - - match parent_node { - // This match arm is for when the def_id appears in a GAT whose - // path can't be resolved without typechecking e.g. - // - // trait Foo { - // type Assoc; - // fn foo() -> Self::Assoc<3>; - // } - // - // In the above code we would call this query with the def_id of 3 and - // the parent_node we match on would be the hir node for Self::Assoc<3> - // - // `Self::Assoc<3>` cant be resolved without typchecking here as we - // didnt write ::Assoc<3>. If we did then another match - // arm would handle this. - // - // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU - Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { - // Find the Item containing the associated type so we can create an ItemCtxt. - // Using the ItemCtxt convert the HIR for the unresolved assoc type into a - // ty which is a fully resolved projection. - // For the code example above, this would mean converting Self::Assoc<3> - // into a ty::Projection(::Assoc<3>) - let item_hir_id = tcx - .hir() - .parent_iter(hir_id) - .filter(|(_, node)| matches!(node, Node::Item(_))) - .map(|(id, _)| id) - .next() - .unwrap(); - let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); - let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; - let ty = item_ctxt.ast_ty_to_ty(hir_ty); - - // Iterate through the generics of the projection to find the one that corresponds to - // the def_id that this query was called with. We filter to only const args here as a - // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't - // but it can't hurt to be safe ^^ - if let ty::Projection(projection) = ty.kind() { - let generics = tcx.generics_of(projection.item_def_id); - - let arg_index = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_const()) - .position(|arg| arg.id() == hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in segment"); - }); + match tcx.hir().get(hir_id) { + Node::AnonConst(_) => (), + _ => return None, + }; - return generics - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) - .nth(arg_index) - .map(|param| param.def_id); - } + let parent_node_id = tcx.hir().get_parent_node(hir_id); + let parent_node = tcx.hir().get(parent_node_id); - // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - "unexpected non-GAT usage of an anon const", - ); - return None; - } - Node::Expr(&Expr { - kind: - ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - }) => { - let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); - let tables = tcx.typeck(body_owner); - // This may fail in case the method/path does not actually exist. - // As there is no relevant param for `def_id`, we simply return - // `None` here. - let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?; - let idx = segment + let (generics, arg_idx) = match parent_node { + // This match arm is for when the def_id appears in a GAT whose + // path can't be resolved without typechecking e.g. + // + // trait Foo { + // type Assoc; + // fn foo() -> Self::Assoc<3>; + // } + // + // In the above code we would call this query with the def_id of 3 and + // the parent_node we match on would be the hir node for Self::Assoc<3> + // + // `Self::Assoc<3>` cant be resolved without typchecking here as we + // didnt write ::Assoc<3>. If we did then another match + // arm would handle this. + // + // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU + Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { + // Find the Item containing the associated type so we can create an ItemCtxt. + // Using the ItemCtxt convert the HIR for the unresolved assoc type into a + // ty which is a fully resolved projection. + // For the code example above, this would mean converting Self::Assoc<3> + // into a ty::Projection(::Assoc<3>) + let item_hir_id = tcx + .hir() + .parent_iter(hir_id) + .filter(|(_, node)| matches!(node, Node::Item(_))) + .map(|(id, _)| id) + .next() + .unwrap(); + let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); + let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; + let ty = item_ctxt.ast_ty_to_ty(hir_ty); + + // Iterate through the generics of the projection to find the one that corresponds to + // the def_id that this query was called with. We filter to only const args here as a + // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't + // but it can't hurt to be safe ^^ + if let ty::Projection(projection) = ty.kind() { + let generics = tcx.generics_of(projection.item_def_id); + + let arg_index = segment .args .and_then(|args| { args.args .iter() - .filter(|arg| arg.is_const()) + .filter(|arg| arg.is_ty_or_const()) .position(|arg| arg.id() == hir_id) }) .unwrap_or_else(|| { bug!("no arg matching AnonConst in segment"); }); - tcx.generics_of(type_dependent_def) - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) - .nth(idx) - .map(|param| param.def_id) + (generics, arg_index) + } else { + // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + "unexpected non-GAT usage of an anon const", + ); + return None; } + } + Node::Expr(&Expr { + kind: + ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), + .. + }) => { + let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); + let tables = tcx.typeck(body_owner); + // This may fail in case the method/path does not actually exist. + // As there is no relevant param for `def_id`, we simply return + // `None` here. + let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?; + let idx = segment + .args + .and_then(|args| { + args.args + .iter() + .filter(|arg| arg.is_ty_or_const()) + .position(|arg| arg.id() == hir_id) + }) + .unwrap_or_else(|| { + bug!("no arg matching AnonConst in segment"); + }); - Node::Ty(&Ty { kind: TyKind::Path(_), .. }) - | 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, - Node::Expr(&Expr { - kind: - ExprKind::Path(QPath::Resolved(_, path)) - | ExprKind::Struct(&QPath::Resolved(_, path), ..), - .. - }) => { - let body_owner = - tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); - 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.generics_of(type_dependent_def), idx) + } + + Node::Ty(&Ty { kind: TyKind::Path(_), .. }) + | 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, + Node::Expr(&Expr { + kind: + ExprKind::Path(QPath::Resolved(_, path)) + | ExprKind::Struct(&QPath::Resolved(_, path), ..), + .. + }) => { + let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); + 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!("unexpected const parent path {:?}", parent_node), + &format!("unable to find const parent for {} in pat {:?}", hir_id, pat), ); return None; } - }; + } + _ => { + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + &format!("unexpected const parent path {:?}", parent_node), + ); + return None; + } + }; - // 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 filtered = path - .segments + // 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 filtered = path.segments.iter().find_map(|seg| { + seg.args? + .args .iter() - .filter_map(|seg| seg.args.map(|args| (args.args, seg))) - .find_map(|(args, seg)| { - args.iter() - .filter(|arg| arg.is_const()) - .position(|arg| arg.id() == hir_id) - .map(|index| (index, seg)) + .filter(|arg| arg.is_ty_or_const()) + .position(|arg| arg.id() == hir_id) + .map(|index| (index, seg)) + }); + + // FIXME(associated_const_generics): can we blend this with iteration above? + let (arg_index, segment) = match filtered { + None => { + let binding_filtered = path.segments.iter().find_map(|seg| { + seg.args? + .bindings + .iter() + .filter_map(TypeBinding::opt_const) + .position(|ct| ct.hir_id == hir_id) + .map(|idx| (idx, seg)) }); - let (arg_index, segment) = match filtered { - None => { - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - "no arg matching AnonConst in path", - ); - return None; - } - Some(inner) => inner, - }; - - // Try to use the segment resolution if it is valid, otherwise we - // default to the path resolution. - let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); - use def::CtorOf; - let generics = match res { - Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => tcx.generics_of( - tcx.parent(def_id).and_then(|def_id| tcx.parent(def_id)).unwrap(), - ), - Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Struct, _), def_id) => { - tcx.generics_of(tcx.parent(def_id).unwrap()) - } - // Other `DefKind`s don't have generics and would ICE when calling - // `generics_of`. - Res::Def( - DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Trait - | DefKind::OpaqueTy - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::Fn - | DefKind::AssocFn - | DefKind::AssocConst - | DefKind::Impl, - def_id, - ) => tcx.generics_of(def_id), - Res::Err => { - tcx.sess.delay_span_bug(tcx.def_span(def_id), "anon const with Res::Err"); - return None; - } - _ => { - // If the user tries to specify generics on a type that does not take them, - // e.g. `usize`, we may hit this branch, in which case we treat it as if - // no arguments have been passed. An error should already have been emitted. - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - &format!("unexpected anon const res {:?} in path: {:?}", res, path), - ); - return None; + match binding_filtered { + Some(inner) => inner, + None => { + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + "no arg matching AnonConst in path", + ); + return None; + } } - }; + } + Some(inner) => inner, + }; + + // Try to use the segment resolution if it is valid, otherwise we + // default to the path resolution. + let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); + let generics = match tcx.res_generics_def_id(res) { + Some(def_id) => tcx.generics_of(def_id), + None => { + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + &format!("unexpected anon const res {:?} in path: {:?}", res, path), + ); + return None; + } + }; - generics - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) - .nth(arg_index) - .map(|param| param.def_id) + (generics, arg_index) + } + _ => return None, + }; + + debug!(?parent_node); + debug!(?generics, ?arg_idx); + generics + .params + .iter() + .filter(|param| param.kind.is_ty_or_const()) + .nth(match generics.has_self && generics.parent.is_none() { + true => arg_idx + 1, + false => arg_idx, + }) + .and_then(|param| match param.kind { + ty::GenericParamDefKind::Const { .. } => { + debug!(?param); + Some(param.def_id) } _ => None, - } - } else { - None - } + }) } fn get_path_containing_arg_in_pat<'hir>( @@ -280,32 +260,6 @@ fn get_path_containing_arg_in_pat<'hir>( arg_path } -pub(super) fn default_anon_const_substs(tcx: TyCtxt<'_>, def_id: DefId) -> SubstsRef<'_> { - let generics = tcx.generics_of(def_id); - if let Some(parent) = generics.parent { - // This is the reason we bother with having optional anon const substs. - // - // In the future the substs of an anon const will depend on its parents predicates - // at which point eagerly looking at them will cause a query cycle. - // - // So for now this is only an assurance that this approach won't cause cycle errors in - // the future. - let _cycle_check = tcx.predicates_of(parent); - } - - let substs = InternalSubsts::identity_for_item(tcx, def_id); - // We only expect substs with the following type flags as default substs. - // - // Getting this wrong can lead to ICE and unsoundness, so we assert it here. - for arg in substs.iter() { - let allowed_flags = ty::TypeFlags::MAY_NEED_DEFAULT_CONST_SUBSTS - | ty::TypeFlags::STILL_FURTHER_SPECIALIZABLE - | ty::TypeFlags::HAS_ERROR; - assert!(!arg.has_type_flags(!allowed_flags)); - } - substs -} - pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { let def_id = def_id.expect_local(); use rustc_hir::*; @@ -350,7 +304,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } } ImplItemKind::TyAlias(ty) => { - if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id).to_def_id()).is_none() { + if tcx.impl_trait_ref(tcx.hir().get_parent_item(hir_id)).is_none() { check_feature_inherent_assoc_ty(tcx, item.span); } @@ -460,7 +414,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::Ctor(&ref def) | Node::Variant(Variant { data: ref def, .. }) => match *def { VariantData::Unit(..) | VariantData::Struct(..) => { - tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()) + tcx.type_of(tcx.hir().get_parent_item(hir_id)) } VariantData::Tuple(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); @@ -509,20 +463,55 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx - .adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()) + .adt_def(tcx.hir().get_parent_item(hir_id)) .repr .discr_type() .to_ty(tcx), + Node::TraitRef(trait_ref @ &TraitRef { + path, .. + }) if let Some((binding, seg)) = + path + .segments + .iter() + .find_map(|seg| { + seg.args?.bindings + .iter() + .find_map(|binding| if binding.opt_const()?.hir_id == hir_id { + Some((binding, seg)) + } else { + None + }) + }) => + { + let Some(trait_def_id) = trait_ref.trait_def_id() else { + return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); + }; + let assoc_items = tcx.associated_items(trait_def_id); + let assoc_item = assoc_items.find_by_name_and_kind( + tcx, binding.ident, ty::AssocKind::Const, def_id.to_def_id(), + ); + if let Some(assoc_item) = assoc_item { + tcx.type_of(assoc_item.def_id) + } else { + // FIXME(associated_const_equality): add a useful error message here. + tcx.ty_error_with_message( + DUMMY_SP, + "Could not find associated const on trait", + ) + } + } + Node::GenericParam(&GenericParam { hir_id: param_hir_id, kind: GenericParamKind::Const { default: Some(ct), .. }, .. }) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)), - x => tcx.ty_error_with_message( + x => + tcx.ty_error_with_message( DUMMY_SP, - &format!("unexpected const parent in type_of(): {:?}", x), + &format!("unexpected const parent in type_of(): {x:?}"), ), } } @@ -620,17 +609,17 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { err.emit(); } } else { - self.found = Some((span, concrete_type)); + self.found = Some((span, *concrete_type)); } } } } impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::All; - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::All(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if let hir::ExprKind::Closure(..) = ex.kind { @@ -757,7 +746,10 @@ fn infer_placeholder_type<'a>( if !ty.references_error() { // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. // We are typeck and have the real type, so remove that and suggest the actual type. - err.suggestions.clear(); + // FIXME(eddyb) this looks like it should be functionality on `Diagnostic`. + if let Ok(suggestions) = &mut err.suggestions { + suggestions.clear(); + } // Suggesting unnameable types won't help. let mut mk_nameable = MakeNameable::new(tcx); @@ -781,7 +773,7 @@ fn infer_placeholder_type<'a>( err.emit(); } None => { - let mut diag = bad_placeholder(tcx, "type", vec![span], kind); + let mut diag = bad_placeholder(tcx, vec![span], kind); if !ty.references_error() { let mut mk_nameable = MakeNameable::new(tcx); @@ -807,7 +799,7 @@ fn infer_placeholder_type<'a>( } // Typeck doesn't expect erased regions to be returned from `type_of`. - tcx.fold_regions(ty, &mut false, |r, _| match r { + tcx.fold_regions(ty, &mut false, |r, _| match *r { ty::ReErased => tcx.lifetimes.re_static, _ => r, }) diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs index 88877ad785..909c99adab 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_typeck/src/constrained_generic_params.rs @@ -27,13 +27,12 @@ impl From for Parameter { /// Returns the set of parameters constrained by the impl header. pub fn parameters_for_impl<'tcx>( - tcx: TyCtxt<'tcx>, impl_self_ty: Ty<'tcx>, impl_trait_ref: Option>, ) -> FxHashSet { let vec = match impl_trait_ref { - Some(tr) => parameters_for(tcx, &tr, false), - None => parameters_for(tcx, &impl_self_ty, false), + Some(tr) => parameters_for(&tr, false), + None => parameters_for(&impl_self_ty, false), }; vec.into_iter().collect() } @@ -44,26 +43,20 @@ pub fn parameters_for_impl<'tcx>( /// of parameters whose values are needed in order to constrain `ty` - these /// differ, with the latter being a superset, in the presence of projections. pub fn parameters_for<'tcx>( - tcx: TyCtxt<'tcx>, t: &impl TypeFoldable<'tcx>, include_nonconstraining: bool, ) -> Vec { - let mut collector = ParameterCollector { tcx, parameters: vec![], include_nonconstraining }; + let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining }; t.visit_with(&mut collector); collector.parameters } -struct ParameterCollector<'tcx> { - tcx: TyCtxt<'tcx>, +struct ParameterCollector { parameters: Vec, include_nonconstraining: bool, } -impl<'tcx> TypeVisitor<'tcx> for ParameterCollector<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option> { - Some(self.tcx) - } - +impl<'tcx> TypeVisitor<'tcx> for ParameterCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match *t.kind() { ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => { @@ -86,11 +79,11 @@ impl<'tcx> TypeVisitor<'tcx> for ParameterCollector<'tcx> { ControlFlow::CONTINUE } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { - match c.val { + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { + match c.val() { ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => { // Constant expressions are not injective - return c.ty.visit_with(self); + return c.ty().visit_with(self); } ty::ConstKind::Param(data) => { self.parameters.push(Parameter::from(data)); @@ -205,12 +198,12 @@ pub fn setup_constraining_predicates<'tcx>( // `<::Baz as Iterator>::Output = ::Output` // Then the projection only applies if `T` is known, but it still // does not determine `U`. - let inputs = parameters_for(tcx, &projection.projection_ty, true); + let inputs = parameters_for(&projection.projection_ty, true); let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p)); if !relies_only_on_inputs { continue; } - input_parameters.extend(parameters_for(tcx, &projection.ty, false)); + input_parameters.extend(parameters_for(&projection.term, false)); } else { continue; } diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 1ae0ff3036..db1c80ae43 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -17,7 +17,6 @@ use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; -use std::iter; use crate::mem_categorization as mc; @@ -360,17 +359,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - hir::ExprKind::LlvmInlineAsm(ia) => { - for (o, output) in iter::zip(&ia.inner.outputs, ia.outputs_exprs) { - if o.is_indirect { - self.consume_expr(output); - } else { - self.mutate_expr(output); - } - } - self.consume_exprs(ia.inputs_exprs); - } - hir::ExprKind::Continue(..) | hir::ExprKind::Lit(..) | hir::ExprKind::ConstBlock(..) @@ -796,14 +784,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ); match capture_info.capture_kind { - ty::UpvarCapture::ByValue(_) => { + ty::UpvarCapture::ByValue => { self.delegate_consume(&place_with_id, place_with_id.hir_id); } ty::UpvarCapture::ByRef(upvar_borrow) => { self.delegate.borrow( &place_with_id, place_with_id.hir_id, - upvar_borrow.kind, + upvar_borrow, ); } } diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_typeck/src/hir_wf_check.rs index a49eda6572..7b709b302f 100644 --- a/compiler/rustc_typeck/src/hir_wf_check.rs +++ b/compiler/rustc_typeck/src/hir_wf_check.rs @@ -1,6 +1,6 @@ use crate::collect::ItemCtxt; use rustc_hir as hir; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::TraitEngine; @@ -64,10 +64,6 @@ fn diagnostic_hir_wf_check<'tcx>( } impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { - type Map = intravisit::ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { self.tcx.infer_ctxt().enter(|infcx| { let mut fulfill = traits::FulfillmentContext::new(); @@ -184,6 +180,6 @@ impl<'tcx> TypeFolder<'tcx> for EraseAllBoundRegions<'tcx> { self.tcx } fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - if let ty::ReLateBound(..) = r { &ty::ReErased } else { r } + if r.is_late_bound() { self.tcx.lifetimes.re_erased } else { r } } } diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index ae6321de7f..1604908915 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -117,7 +117,7 @@ fn enforce_impl_params_are_constrained( let impl_predicates = tcx.predicates_of(impl_def_id); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); - let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref); + let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref); cgp::identify_constrained_generic_params( tcx, impl_predicates, @@ -134,7 +134,7 @@ fn enforce_impl_params_are_constrained( match item.kind { ty::AssocKind::Type => { if item.defaultness.has_value() { - cgp::parameters_for(tcx, &tcx.type_of(def_id), true) + cgp::parameters_for(&tcx.type_of(def_id), true) } else { Vec::new() } 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 4fb422c801..92f88a15ee 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -199,22 +199,22 @@ fn unconstrained_parent_impl_substs<'tcx>( for (predicate, _) in impl_generic_predicates.predicates.iter() { if let ty::PredicateKind::Projection(proj) = predicate.kind().skip_binder() { let projection_ty = proj.projection_ty; - let projected_ty = proj.ty; + let projected_ty = proj.term; let unbound_trait_ref = projection_ty.trait_ref(tcx); if Some(unbound_trait_ref) == impl_trait_ref { continue; } - unconstrained_parameters.extend(cgp::parameters_for(tcx, &projection_ty, true)); + unconstrained_parameters.extend(cgp::parameters_for(&projection_ty, true)); - for param in cgp::parameters_for(tcx, &projected_ty, false) { + for param in cgp::parameters_for(&projected_ty, false) { if !unconstrained_parameters.contains(¶m) { constrained_params.insert(param.0); } } - unconstrained_parameters.extend(cgp::parameters_for(tcx, &projected_ty, true)); + unconstrained_parameters.extend(cgp::parameters_for(&projected_ty, true)); } } @@ -248,7 +248,7 @@ fn check_duplicate_params<'tcx>( parent_substs: &Vec>, span: Span, ) { - let mut base_params = cgp::parameters_for(tcx, parent_substs, true); + let mut base_params = cgp::parameters_for(parent_substs, true); base_params.sort_by_key(|param| param.0); if let (_, [duplicate, ..]) = base_params.partition_dedup() { let param = impl1_substs[duplicate.0 as usize]; @@ -269,7 +269,7 @@ fn check_static_lifetimes<'tcx>( parent_substs: &Vec>, span: Span, ) { - if tcx.any_free_region_meets(parent_substs, |r| *r == ty::ReStatic) { + if tcx.any_free_region_meets(parent_substs, |r| r.is_static()) { tcx.sess.struct_span_err(span, "cannot specialize on `'static` lifetime").emit(); } } @@ -376,7 +376,7 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc 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(tcx) => (), + _ if predicate.is_global() => (), // We allow specializing on explicitly marked traits with no associated // items. ty::PredicateKind::Trait(ty::TraitPredicate { diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 24e427f4bc..d415e37ff0 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -69,6 +69,7 @@ This API is completely unstable and subject to change. #![feature(control_flow_enum)] #![feature(hash_drain_filter)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] #[macro_use] extern crate tracing; @@ -121,7 +122,7 @@ use bounds::Bounds; fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { match (decl.c_variadic, abi) { // The function has the correct calling convention, or isn't a "C-variadic" function. - (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl) => {} + (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {} // The function is a "C-variadic" function with an incorrect calling convention. (true, _) => { let mut err = struct_span_err!( @@ -543,8 +544,7 @@ pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { // In case there are any projections, etc., find the "environment" // def-ID that will be used to determine the traits/predicates in // scope. This is derived from the enclosing item-like thing. - let env_node_id = tcx.hir().get_parent_item(hir_ty.hir_id); - let env_def_id = tcx.hir().local_def_id(env_node_id); + let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id); let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); >::ast_ty_to_ty(&item_cx, hir_ty) } @@ -557,8 +557,7 @@ pub fn hir_trait_to_predicates<'tcx>( // In case there are any projections, etc., find the "environment" // def-ID that will be used to determine the traits/predicates in // scope. This is derived from the enclosing item-like thing. - let env_hir_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id); - let env_def_id = tcx.hir().local_def_id(env_hir_id); + let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id); let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); let mut bounds = Bounds::default(); let _ = >::instantiate_poly_trait_ref( diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 440ce04e61..1bbd6d2929 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -192,7 +192,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { if let Some(vec) = self.typeck_results.pat_adjustments().get(pat.hir_id) { if let Some(first_ty) = vec.first() { debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty); - return Ok(first_ty); + return Ok(*first_ty); } } @@ -378,7 +378,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) | hir::ExprKind::InlineAsm(..) - | hir::ExprKind::LlvmInlineAsm(..) | hir::ExprKind::Box(..) | hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), } @@ -563,7 +562,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfCtor(..) - | Res::SelfTy(..) => { + | Res::SelfTy { .. } => { // Structs and Unions have only have one variant. Ok(VariantIdx::new(0)) } diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs index 86d712e2d7..89f0bd8d42 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs @@ -114,18 +114,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( required_predicates: &mut RequiredPredicates<'tcx>, explicit_map: &mut ExplicitPredicatesMap<'tcx>, ) { - // We must not look into the default substs of consts - // as computing those depends on the results of `predicates_of`. - // - // Luckily the only types contained in default substs are type - // parameters which don't matter here. - // - // FIXME(adt_const_params): Once complex const parameter types - // are allowed, this might be incorrect. I think that we will still be - // fine, as all outlives relations of the const param types should also - // be part of the adt containing it, but we should still both update the - // documentation and add some tests for this. - for arg in field_ty.walk_ignoring_default_const_substs() { + for arg in field_ty.walk() { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -317,7 +306,7 @@ pub fn check_explicit_predicates<'tcx>( // 'b`. if let Some(self_ty) = ignored_self_ty { if let GenericArgKind::Type(ty) = outlives_predicate.0.unpack() { - if ty.walk(tcx).any(|arg| arg == self_ty.into()) { + if ty.walk().any(|arg| arg == self_ty.into()) { debug!("skipping self ty = {:?}", &ty); continue; } diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs index eb3853b6b3..139be8a42d 100644 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ b/compiler/rustc_typeck/src/outlives/mod.rs @@ -35,8 +35,7 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate // parent of generics returned by `generics_of` // // In the above code we want the anon const to have predicates in its param env for `'b: 'a` - let item_id = tcx.hir().get_parent_item(id); - let item_def_id = tcx.hir().local_def_id(item_id).to_def_id(); + let item_def_id = tcx.hir().get_parent_item(id); // In the above code example we would be calling `inferred_outlives_of(Foo)` here return tcx.inferred_outlives_of(item_def_id); } @@ -106,14 +105,14 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { match kind1.unpack() { GenericArgKind::Type(ty1) => Some(( ty::Binder::dummy(ty::PredicateKind::TypeOutlives( - ty::OutlivesPredicate(ty1, region2), + ty::OutlivesPredicate(ty1, *region2), )) .to_predicate(tcx), span, )), GenericArgKind::Lifetime(region1) => Some(( ty::Binder::dummy(ty::PredicateKind::RegionOutlives( - ty::OutlivesPredicate(region1, region2), + ty::OutlivesPredicate(region1, *region2), )) .to_predicate(tcx), span, diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs index 76ae2ee435..54a5037b57 100644 --- a/compiler/rustc_typeck/src/outlives/utils.rs +++ b/compiler/rustc_typeck/src/outlives/utils.rs @@ -1,6 +1,6 @@ use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, Region, RegionKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, Region, Ty, TyCtxt}; use rustc_span::Span; use smallvec::smallvec; use std::collections::BTreeMap; @@ -133,7 +133,7 @@ pub fn insert_outlives_predicate<'tcx>( fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool { // First, screen for regions that might appear in a type header. - match region { + match *region { // These correspond to `T: 'a` relationships: // // struct Foo<'a, T> { @@ -141,7 +141,7 @@ fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool { // } // // We care about these, so fall through. - RegionKind::ReEarlyBound(_) => true, + ty::ReEarlyBound(_) => true, // These correspond to `T: 'static` relationships which can be // rather surprising. We are therefore putting this behind a @@ -150,7 +150,7 @@ fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool { // struct Foo<'a, T> { // field: &'static T, // this would generate a ReStatic // } - RegionKind::ReStatic => tcx.sess.features_untracked().infer_static_outlives_requirements, + ty::ReStatic => tcx.sess.features_untracked().infer_static_outlives_requirements, // Late-bound regions can appear in `fn` types: // @@ -160,19 +160,16 @@ fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool { // // The type above might generate a `T: 'b` bound, but we can // ignore it. We can't put it on the struct header anyway. - RegionKind::ReLateBound(..) => false, + ty::ReLateBound(..) => false, // This can appear in `where Self: ` bounds (#64855): // // struct Bar(::Type) where Self: ; // struct Baz<'a>(&'a Self) where Self: ; - RegionKind::ReEmpty(_) => false, + ty::ReEmpty(_) => false, // These regions don't appear in types from type declarations: - RegionKind::ReErased - | RegionKind::ReVar(..) - | RegionKind::RePlaceholder(..) - | RegionKind::ReFree(..) => { + ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => { bug!("unexpected region in outlives inference: {:?}", region); } } diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs index 33c27ce86d..1c8f848cf2 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_typeck/src/variance/constraints.rs @@ -308,11 +308,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } for projection in data.projection_bounds() { - self.add_constraints_from_ty( - current, - projection.skip_binder().ty, - self.invariant, - ); + match projection.skip_binder().term { + ty::Term::Ty(ty) => { + self.add_constraints_from_ty(current, ty, self.invariant); + } + ty::Term::Const(c) => { + self.add_constraints_from_const(current, c, self.invariant) + } + } } } @@ -398,15 +401,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { fn add_constraints_from_const( &mut self, current: &CurrentItem, - val: &ty::Const<'tcx>, + val: ty::Const<'tcx>, variance: VarianceTermPtr<'a>, ) { debug!("add_constraints_from_const(val={:?}, variance={:?})", val, variance); - match &val.val { + match &val.val() { ty::ConstKind::Unevaluated(uv) => { - let substs = uv.substs(self.tcx()); - self.add_constraints_from_invariant_substs(current, substs, variance); + self.add_constraints_from_invariant_substs(current, uv.substs, variance); } _ => {} } diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_typeck/src/variance/terms.rs index d7f9df668b..36fbfc21ff 100644 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ b/compiler/rustc_typeck/src/variance/terms.rs @@ -86,7 +86,7 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( fn lang_items(tcx: TyCtxt<'_>) -> Vec<(hir::HirId, Vec)> { let lang_items = tcx.lang_items(); - let all = vec![ + let all = [ (lang_items.phantom_data(), vec![ty::Covariant]), (lang_items.unsafe_cell_type(), vec![ty::Invariant]), ]; diff --git a/config.toml.example b/config.toml.example index f24f8e81a7..ad48cc881f 100644 --- a/config.toml.example +++ b/config.toml.example @@ -157,6 +157,9 @@ changelog-seen = 2 # Whether to build the clang compiler. #clang = false +# Custom CMake defines to set when building LLVM. +#build-config = {} + # ============================================================================= # General build configuration options # ============================================================================= @@ -289,7 +292,7 @@ changelog-seen = 2 #sanitizers = false # Build the profiler runtime (required when compiling with options that depend -# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). +# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`). #profiler = false # Indicates whether the native libraries linked into Cargo will be statically @@ -671,7 +674,7 @@ changelog-seen = 2 #sanitizers = build.sanitizers (bool) # Build the profiler runtime for this target(required when compiling with options that depend -# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). +# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`). # This option will override the same option under [build] section. #profiler = build.profiler (bool) diff --git a/git-commit-hash b/git-commit-hash index 9d9179cf7d..53eeb6939b 100644 --- a/git-commit-hash +++ b/git-commit-hash @@ -1 +1 @@ -9d1b2106e23b1abd32fce1f17267604a5102f57a \ No newline at end of file +7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c \ No newline at end of file diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index b3ff0fd0a3..265020209e 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/rust-lang/rust.git" description = "The Rust core allocation and collections library" autotests = false autobenches = false -edition = "2018" +edition = "2021" [dependencies] core = { path = "../core" } diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index d075658f51..9d4f9af91a 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -348,7 +348,6 @@ extern "Rust" { // This is the magic symbol to call the global alloc error handler. rustc generates // it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the // default implementations below (`__rdl_oom`) otherwise. - #[rustc_allocator_nounwind] fn __rust_alloc_error_handler(size: usize, align: usize) -> !; } @@ -367,7 +366,6 @@ extern "Rust" { #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_const_unstable(feature = "const_alloc_error", issue = "92523")] #[cfg(all(not(no_global_oom_handling), not(test)))] -#[rustc_allocator_nounwind] #[cold] pub const fn handle_alloc_error(layout: Layout) -> ! { const fn ct_error(_: Layout) -> ! { @@ -398,13 +396,13 @@ pub mod __alloc_error_handler { // if there is no `#[alloc_error_handler]` #[rustc_std_internal_symbol] - pub unsafe extern "C" fn __rdl_oom(size: usize, _align: usize) -> ! { + pub unsafe extern "C-unwind" fn __rdl_oom(size: usize, _align: usize) -> ! { panic!("memory allocation of {} bytes failed", size) } // if there is an `#[alloc_error_handler]` #[rustc_std_internal_symbol] - pub unsafe extern "C" fn __rg_oom(size: usize, align: usize) -> ! { + pub unsafe extern "C-unwind" fn __rg_oom(size: usize, align: usize) -> ! { let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; extern "Rust" { #[lang = "oom"] diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index aa7344ba40..f753189c68 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -133,6 +133,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::any::Any; +use core::async_iter::AsyncIterator; use core::borrow; use core::cmp::Ordering; use core::convert::{From, TryFrom}; @@ -149,7 +150,6 @@ use core::ops::{ }; use core::pin::Pin; use core::ptr::{self, Unique}; -use core::stream::Stream; use core::task::{Context, Poll}; #[cfg(not(no_global_oom_handling))] @@ -1170,8 +1170,7 @@ impl Box { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_box", issue = "92521")] -unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> const Drop for Box { +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box { fn drop(&mut self) { // FIXME: Do nothing, drop is currently performed by compiler. } @@ -1992,8 +1991,8 @@ where } } -#[unstable(feature = "async_stream", issue = "79024")] -impl Stream for Box { +#[unstable(feature = "async_iterator", issue = "79024")] +impl AsyncIterator for Box { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs index 6fc6002d55..e18cd8cd46 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -433,7 +433,7 @@ impl BinaryHeap { /// /// ``` /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// let mut heap = BinaryHeap::from([1, 3]); /// /// assert_eq!(heap.pop(), Some(3)); /// assert_eq!(heap.pop(), Some(1)); @@ -506,7 +506,7 @@ impl BinaryHeap { /// ``` /// use std::collections::BinaryHeap; /// - /// let mut heap = BinaryHeap::from(vec![1, 2, 4, 5, 7]); + /// let mut heap = BinaryHeap::from([1, 2, 4, 5, 7]); /// heap.push(6); /// heap.push(3); /// @@ -725,11 +725,8 @@ impl BinaryHeap { /// ``` /// use std::collections::BinaryHeap; /// - /// let v = vec![-10, 1, 2, 3, 3]; - /// let mut a = BinaryHeap::from(v); - /// - /// let v = vec![-20, 5, 43]; - /// let mut b = BinaryHeap::from(v); + /// let mut a = BinaryHeap::from([-10, 1, 2, 3, 3]); + /// let mut b = BinaryHeap::from([-20, 5, 43]); /// /// a.append(&mut b); /// @@ -749,9 +746,12 @@ impl BinaryHeap { self.rebuild_tail(start); } - /// Returns an iterator which retrieves elements in heap order. - /// The retrieved elements are removed from the original heap. - /// The remaining elements will be removed on drop in heap order. + /// Clears the binary heap, returning an iterator over the removed elements + /// in heap order. If the iterator is dropped before being fully consumed, + /// it drops the remaining elements in heap order. + /// + /// The returned iterator keeps a mutable borrow on the heap to optimize + /// its implementation. /// /// Note: /// * `.drain_sorted()` is *O*(*n* \* log(*n*)); much slower than `.drain()`. @@ -765,7 +765,7 @@ impl BinaryHeap { /// #![feature(binary_heap_drain_sorted)] /// use std::collections::BinaryHeap; /// - /// let mut heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); + /// let mut heap = BinaryHeap::from([1, 2, 3, 4, 5]); /// assert_eq!(heap.len(), 5); /// /// drop(heap.drain_sorted()); // removes all elements in heap order @@ -790,7 +790,7 @@ impl BinaryHeap { /// #![feature(binary_heap_retain)] /// use std::collections::BinaryHeap; /// - /// let mut heap = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]); + /// let mut heap = BinaryHeap::from([-10, -5, 1, 2, 4, 13]); /// /// heap.retain(|x| x % 2 == 0); // only keep even numbers /// @@ -826,7 +826,7 @@ impl BinaryHeap { /// /// ``` /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); + /// let heap = BinaryHeap::from([1, 2, 3, 4]); /// /// // Print 1, 2, 3, 4 in arbitrary order /// for x in heap.iter() { @@ -848,9 +848,9 @@ impl BinaryHeap { /// ``` /// #![feature(binary_heap_into_iter_sorted)] /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); + /// let heap = BinaryHeap::from([1, 2, 3, 4, 5]); /// - /// assert_eq!(heap.into_iter_sorted().take(2).collect::>(), vec![5, 4]); + /// assert_eq!(heap.into_iter_sorted().take(2).collect::>(), [5, 4]); /// ``` #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] pub fn into_iter_sorted(self) -> IntoIterSorted { @@ -1086,7 +1086,7 @@ impl BinaryHeap { /// use std::collections::BinaryHeap; /// use std::io::{self, Write}; /// - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); + /// let heap = BinaryHeap::from([1, 2, 3, 4, 5, 6, 7]); /// /// io::sink().write(heap.as_slice()).unwrap(); /// ``` @@ -1105,7 +1105,7 @@ impl BinaryHeap { /// /// ``` /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); + /// let heap = BinaryHeap::from([1, 2, 3, 4, 5, 6, 7]); /// let vec = heap.into_vec(); /// /// // Will print in some order @@ -1127,7 +1127,7 @@ impl BinaryHeap { /// /// ``` /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 3]); + /// let heap = BinaryHeap::from([1, 3]); /// /// assert_eq!(heap.len(), 2); /// ``` @@ -1161,9 +1161,12 @@ impl BinaryHeap { self.len() == 0 } - /// Clears the binary heap, returning an iterator over the removed elements. + /// Clears the binary heap, returning an iterator over the removed elements + /// in arbitrary order. If the iterator is dropped before being fully + /// consumed, it drops the remaining elements in arbitrary order. /// - /// The elements are removed in arbitrary order. + /// The returned iterator keeps a mutable borrow on the heap to optimize + /// its implementation. /// /// # Examples /// @@ -1171,7 +1174,7 @@ impl BinaryHeap { /// /// ``` /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// let mut heap = BinaryHeap::from([1, 3]); /// /// assert!(!heap.is_empty()); /// @@ -1195,7 +1198,7 @@ impl BinaryHeap { /// /// ``` /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// let mut heap = BinaryHeap::from([1, 3]); /// /// assert!(!heap.is_empty()); /// @@ -1616,7 +1619,7 @@ impl IntoIterator for BinaryHeap { /// /// ``` /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); + /// let heap = BinaryHeap::from([1, 2, 3, 4]); /// /// // Print 1, 2, 3, 4 in arbitrary order /// for x in heap.into_iter() { diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 199c05dc5d..67f5b386ec 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -16,7 +16,10 @@ use super::node::{self, marker, ForceResult::*, Handle, NodeRef, Root}; use super::search::SearchResult::*; mod entry; + +#[stable(feature = "rust1", since = "1.0.0")] pub use entry::{Entry, OccupiedEntry, OccupiedError, VacantEntry}; + use Entry::*; /// Minimum number of elements in a node that is not a root. @@ -31,7 +34,7 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; // An empty map is represented either by the absence of a root node or by a // root node that is an empty leaf. -/// A map based on a [B-Tree]. +/// An ordered map based on a [B-Tree]. /// /// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing /// the amount of work performed in a search. In theory, a binary search tree (BST) is the optimal @@ -65,6 +68,10 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// incorrect results, aborts, memory leaks, or non-termination) but will not be undefined /// behavior. /// +/// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::values`], or +/// [`BTreeMap::keys`] produce their items in order by key, and take worst-case logarithmic and +/// amortized constant time per item returned. +/// /// [B-Tree]: https://en.wikipedia.org/wiki/B-tree /// [`Cell`]: core::cell::Cell /// [`RefCell`]: core::cell::RefCell @@ -1091,10 +1098,8 @@ impl BTreeMap { /// ``` /// use std::collections::BTreeMap; /// - /// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"] - /// .iter() - /// .map(|&s| (s, 0)) - /// .collect(); + /// let mut map: BTreeMap<&str, i32> = + /// [("Alice", 0), ("Bob", 0), ("Carol", 0), ("Cheryl", 0)].into(); /// for (_, balance) in map.range_mut("B".."Cheryl") { /// *balance += 100; /// } @@ -1128,7 +1133,7 @@ impl BTreeMap { /// let mut count: BTreeMap<&str, usize> = BTreeMap::new(); /// /// // count the number of occurrences of letters in the vec - /// for x in vec!["a", "b", "a", "c", "a", "b"] { + /// for x in ["a", "b", "a", "c", "a", "b"] { /// *count.entry(x).or_insert(0) += 1; /// } /// @@ -1228,8 +1233,8 @@ impl BTreeMap { /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); /// let evens: BTreeMap<_, _> = map.drain_filter(|k, _v| k % 2 == 0).collect(); /// let odds = map; - /// assert_eq!(evens.keys().copied().collect::>(), vec![0, 2, 4, 6]); - /// assert_eq!(odds.keys().copied().collect::>(), vec![1, 3, 5, 7]); + /// assert_eq!(evens.keys().copied().collect::>(), [0, 2, 4, 6]); + /// assert_eq!(odds.keys().copied().collect::>(), [1, 3, 5, 7]); /// ``` #[unstable(feature = "btree_drain_filter", issue = "70530")] pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> @@ -2047,6 +2052,8 @@ where #[stable(feature = "std_collections_from_array", since = "1.56.0")] impl From<[(K, V); N]> for BTreeMap { + /// Converts a `[(K, V); N]` into a `BTreeMap<(K, V)>`. + /// /// ``` /// use std::collections::BTreeMap; /// diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index c95aeeaa60..b39b5409ae 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -728,7 +728,7 @@ fn test_range_large() { #[test] fn test_range_inclusive_max_value() { let max = usize::MAX; - let map: BTreeMap<_, _> = vec![(max, 0)].into_iter().collect(); + let map: BTreeMap<_, _> = [(max, 0)].into_iter().collect(); assert_eq!(map.range(max..=max).collect::>(), &[(&max, &0)]); } @@ -2128,7 +2128,7 @@ fn test_into_iter_drop_leak_height_1() { #[test] fn test_into_keys() { - let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')]; + let vec = [(1, 'a'), (2, 'b'), (3, 'c')]; let map: BTreeMap<_, _> = vec.into_iter().collect(); let keys: Vec<_> = map.into_keys().collect(); diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 394c21bf51..a4315be74e 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -15,7 +15,7 @@ use super::Recover; // FIXME(conventions): implement bounded iterators -/// A set based on a B-Tree. +/// An ordered set based on a B-Tree. /// /// See [`BTreeMap`]'s documentation for a detailed discussion of this collection's performance /// benefits and drawbacks. @@ -27,6 +27,9 @@ use super::Recover; /// incorrect results, aborts, memory leaks, or non-termination) but will not be undefined /// behavior. /// +/// Iterators returned by [`BTreeSet::iter`] produce their items in order, and take worst-case +/// logarithmic and amortized constant time per item returned. +/// /// [`Ord`]: core::cmp::Ord /// [`Cell`]: core::cell::Cell /// [`RefCell`]: core::cell::RefCell @@ -1094,6 +1097,8 @@ impl FromIterator for BTreeSet { #[stable(feature = "std_collections_from_array", since = "1.56.0")] impl From<[T; N]> for BTreeSet { + /// Converts a `[T; N]` into a `BTreeSet`. + /// /// ``` /// use std::collections::BTreeSet; /// diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 4a07d5d4be..d81f24e720 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1953,6 +1953,8 @@ impl Hash for LinkedList { #[stable(feature = "std_collections_from_array", since = "1.56.0")] impl From<[T; N]> for LinkedList { + /// Converts a `[T; N]` into a `LinkedList`. + /// /// ``` /// use std::collections::LinkedList; /// diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 1ea135a2ae..628a5b1556 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -14,7 +14,7 @@ pub mod vec_deque; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub mod btree_map { - //! A map based on a B-Tree. + //! An ordered map based on a B-Tree. #[stable(feature = "rust1", since = "1.0.0")] pub use super::btree::map::*; } @@ -22,7 +22,7 @@ pub mod btree_map { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub mod btree_set { - //! A set based on a B-Tree. + //! An ordered set based on a B-Tree. #[stable(feature = "rust1", since = "1.0.0")] pub use super::btree::set::*; } diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 075becfb7d..7139a0fb94 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1,4 +1,4 @@ -//! A double-ended queue implemented with a growable ring buffer. +//! A double-ended queue (deque) implemented with a growable ring buffer. //! //! This queue has *O*(1) amortized inserts and removals from both ends of the //! container. It also has *O*(1) indexing like a vector. The contained elements @@ -156,7 +156,7 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for VecDeque { #[stable(feature = "rust1", since = "1.0.0")] impl Default for VecDeque { - /// Creates an empty `VecDeque`. + /// Creates an empty deque. #[inline] fn default() -> VecDeque { VecDeque::new() @@ -483,14 +483,14 @@ impl VecDeque { } impl VecDeque { - /// Creates an empty `VecDeque`. + /// Creates an empty deque. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let vector: VecDeque = VecDeque::new(); + /// let deque: VecDeque = VecDeque::new(); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -499,14 +499,14 @@ impl VecDeque { VecDeque::new_in(Global) } - /// Creates an empty `VecDeque` with space for at least `capacity` elements. + /// Creates an empty deque with space for at least `capacity` elements. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let vector: VecDeque = VecDeque::with_capacity(10); + /// let deque: VecDeque = VecDeque::with_capacity(10); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -517,14 +517,14 @@ impl VecDeque { } impl VecDeque { - /// Creates an empty `VecDeque`. + /// Creates an empty deque. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let vector: VecDeque = VecDeque::new(); + /// let deque: VecDeque = VecDeque::new(); /// ``` #[inline] #[unstable(feature = "allocator_api", issue = "32838")] @@ -532,14 +532,14 @@ impl VecDeque { VecDeque::with_capacity_in(INITIAL_CAPACITY, alloc) } - /// Creates an empty `VecDeque` with space for at least `capacity` elements. + /// Creates an empty deque with space for at least `capacity` elements. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let vector: VecDeque = VecDeque::with_capacity(10); + /// let deque: VecDeque = VecDeque::with_capacity(10); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque { @@ -636,7 +636,7 @@ impl VecDeque { unsafe { ptr::swap(self.ptr().add(ri), self.ptr().add(rj)) } } - /// Returns the number of elements the `VecDeque` can hold without + /// Returns the number of elements the deque can hold without /// reallocating. /// /// # Examples @@ -654,7 +654,7 @@ impl VecDeque { } /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the - /// given `VecDeque`. Does nothing if the capacity is already sufficient. + /// given deque. Does nothing if the capacity is already sufficient. /// /// Note that the allocator may give the collection more space than it requests. Therefore /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future @@ -669,7 +669,7 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque = vec![1].into_iter().collect(); + /// let mut buf: VecDeque = [1].into(); /// buf.reserve_exact(10); /// assert!(buf.capacity() >= 11); /// ``` @@ -681,7 +681,7 @@ impl VecDeque { } /// Reserves capacity for at least `additional` more elements to be inserted in the given - /// `VecDeque`. The collection may reserve more space to avoid frequent reallocations. + /// deque. The collection may reserve more space to avoid frequent reallocations. /// /// # Panics /// @@ -692,7 +692,7 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque = vec![1].into_iter().collect(); + /// let mut buf: VecDeque = [1].into(); /// buf.reserve(10); /// assert!(buf.capacity() >= 11); /// ``` @@ -714,7 +714,7 @@ impl VecDeque { } /// Tries to reserve the minimum capacity for exactly `additional` more elements to - /// be inserted in the given `VecDeque`. After calling `try_reserve_exact`, + /// be inserted in the given deque. After calling `try_reserve_exact`, /// capacity will be greater than or equal to `self.len() + additional`. /// Does nothing if the capacity is already sufficient. /// @@ -756,7 +756,7 @@ impl VecDeque { } /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the given `VecDeque`. The collection may reserve more space to avoid + /// in the given deque. The collection may reserve more space to avoid /// frequent reallocations. After calling `try_reserve`, capacity will be /// greater than or equal to `self.len() + additional`. Does nothing if /// capacity is already sufficient. @@ -805,10 +805,10 @@ impl VecDeque { Ok(()) } - /// Shrinks the capacity of the `VecDeque` as much as possible. + /// Shrinks the capacity of the deque as much as possible. /// /// It will drop down as close as possible to the length but the allocator may still inform the - /// `VecDeque` that there is space for a few more elements. + /// deque that there is space for a few more elements. /// /// # Examples /// @@ -826,7 +826,7 @@ impl VecDeque { self.shrink_to(0); } - /// Shrinks the capacity of the `VecDeque` with a lower bound. + /// Shrinks the capacity of the deque with a lower bound. /// /// The capacity will remain at least as large as both the length /// and the supplied value. @@ -909,10 +909,10 @@ impl VecDeque { } } - /// Shortens the `VecDeque`, keeping the first `len` elements and dropping + /// Shortens the deque, keeping the first `len` elements and dropping /// the rest. /// - /// If `len` is greater than the `VecDeque`'s current length, this has no + /// If `len` is greater than the deque's current length, this has no /// effect. /// /// # Examples @@ -1027,10 +1027,10 @@ impl VecDeque { } /// Returns a pair of slices which contain, in order, the contents of the - /// `VecDeque`. + /// deque. /// /// If [`make_contiguous`] was previously called, all elements of the - /// `VecDeque` will be in the first slice and the second slice will be empty. + /// deque will be in the first slice and the second slice will be empty. /// /// [`make_contiguous`]: VecDeque::make_contiguous /// @@ -1039,18 +1039,18 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut vector = VecDeque::new(); + /// let mut deque = VecDeque::new(); /// - /// vector.push_back(0); - /// vector.push_back(1); - /// vector.push_back(2); + /// deque.push_back(0); + /// deque.push_back(1); + /// deque.push_back(2); /// - /// assert_eq!(vector.as_slices(), (&[0, 1, 2][..], &[][..])); + /// assert_eq!(deque.as_slices(), (&[0, 1, 2][..], &[][..])); /// - /// vector.push_front(10); - /// vector.push_front(9); + /// deque.push_front(10); + /// deque.push_front(9); /// - /// assert_eq!(vector.as_slices(), (&[9, 10][..], &[0, 1, 2][..])); + /// assert_eq!(deque.as_slices(), (&[9, 10][..], &[0, 1, 2][..])); /// ``` #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] @@ -1062,10 +1062,10 @@ impl VecDeque { } /// Returns a pair of slices which contain, in order, the contents of the - /// `VecDeque`. + /// deque. /// /// If [`make_contiguous`] was previously called, all elements of the - /// `VecDeque` will be in the first slice and the second slice will be empty. + /// deque will be in the first slice and the second slice will be empty. /// /// [`make_contiguous`]: VecDeque::make_contiguous /// @@ -1074,17 +1074,17 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut vector = VecDeque::new(); + /// let mut deque = VecDeque::new(); /// - /// vector.push_back(0); - /// vector.push_back(1); + /// deque.push_back(0); + /// deque.push_back(1); /// - /// vector.push_front(10); - /// vector.push_front(9); + /// deque.push_front(10); + /// deque.push_front(9); /// - /// vector.as_mut_slices().0[0] = 42; - /// vector.as_mut_slices().1[0] = 24; - /// assert_eq!(vector.as_slices(), (&[42, 10][..], &[24, 1][..])); + /// deque.as_mut_slices().0[0] = 42; + /// deque.as_mut_slices().1[0] = 24; + /// assert_eq!(deque.as_slices(), (&[42, 10][..], &[24, 1][..])); /// ``` #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] @@ -1097,34 +1097,34 @@ impl VecDeque { } } - /// Returns the number of elements in the `VecDeque`. + /// Returns the number of elements in the deque. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let mut v = VecDeque::new(); - /// assert_eq!(v.len(), 0); - /// v.push_back(1); - /// assert_eq!(v.len(), 1); + /// let mut deque = VecDeque::new(); + /// assert_eq!(deque.len(), 0); + /// deque.push_back(1); + /// assert_eq!(deque.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { count(self.tail, self.head, self.cap()) } - /// Returns `true` if the `VecDeque` is empty. + /// Returns `true` if the deque is empty. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let mut v = VecDeque::new(); - /// assert!(v.is_empty()); - /// v.push_front(1); - /// assert!(!v.is_empty()); + /// let mut deque = VecDeque::new(); + /// assert!(deque.is_empty()); + /// deque.push_front(1); + /// assert!(!deque.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn is_empty(&self) -> bool { @@ -1141,24 +1141,24 @@ impl VecDeque { (tail, head) } - /// Creates an iterator that covers the specified range in the `VecDeque`. + /// Creates an iterator that covers the specified range in the deque. /// /// # Panics /// /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. + /// the end point is greater than the length of the deque. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); - /// let range = v.range(2..).copied().collect::>(); + /// let deque: VecDeque<_> = [1, 2, 3].into(); + /// let range = deque.range(2..).copied().collect::>(); /// assert_eq!(range, [3]); /// /// // A full range covers all contents - /// let all = v.range(..); + /// let all = deque.range(..); /// assert_eq!(all.len(), 3); /// ``` #[inline] @@ -1176,29 +1176,29 @@ impl VecDeque { } } - /// Creates an iterator that covers the specified mutable range in the `VecDeque`. + /// Creates an iterator that covers the specified mutable range in the deque. /// /// # Panics /// /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. + /// the end point is greater than the length of the deque. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); - /// for v in v.range_mut(2..) { + /// let mut deque: VecDeque<_> = [1, 2, 3].into(); + /// for v in deque.range_mut(2..) { /// *v *= 2; /// } - /// assert_eq!(v, vec![1, 2, 6]); + /// assert_eq!(deque, [1, 2, 6]); /// /// // A full range covers all contents - /// for v in v.range_mut(..) { + /// for v in deque.range_mut(..) { /// *v *= 2; /// } - /// assert_eq!(v, vec![2, 4, 12]); + /// assert_eq!(deque, [2, 4, 12]); /// ``` #[inline] #[stable(feature = "deque_range", since = "1.51.0")] @@ -1215,34 +1215,38 @@ impl VecDeque { unsafe { IterMut::new(ring, tail, head, PhantomData) } } - /// Creates a draining iterator that removes the specified range in the - /// `VecDeque` and yields the removed items. + /// Removes the specified range from the deque in bulk, returning all + /// removed elements as an iterator. If the iterator is dropped before + /// being fully consumed, it drops the remaining removed elements. /// - /// Note 1: The element range is removed even if the iterator is not - /// consumed until the end. + /// The returned iterator keeps a mutable borrow on the queue to optimize + /// its implementation. /// - /// Note 2: It is unspecified how many elements are removed from the deque, - /// if the `Drain` value is not dropped, but the borrow it holds expires - /// (e.g., due to `mem::forget`). /// /// # Panics /// /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. + /// the end point is greater than the length of the deque. + /// + /// # Leaking + /// + /// If the returned iterator goes out of scope without being dropped (due to + /// [`mem::forget`], for example), the deque may have lost and leaked + /// elements arbitrarily, including elements outside the range. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); - /// let drained = v.drain(2..).collect::>(); + /// let mut deque: VecDeque<_> = [1, 2, 3].into(); + /// let drained = deque.drain(2..).collect::>(); /// assert_eq!(drained, [3]); - /// assert_eq!(v, [1, 2]); + /// assert_eq!(deque, [1, 2]); /// - /// // A full range clears all contents - /// v.drain(..); - /// assert!(v.is_empty()); + /// // A full range clears all contents, like `clear()` does + /// deque.drain(..); + /// assert!(deque.is_empty()); /// ``` #[inline] #[stable(feature = "drain", since = "1.6.0")] @@ -1297,17 +1301,17 @@ impl VecDeque { unsafe { Drain::new(drain_head, head, iter, deque) } } - /// Clears the `VecDeque`, removing all values. + /// Clears the deque, removing all values. /// /// # Examples /// /// ``` /// use std::collections::VecDeque; /// - /// let mut v = VecDeque::new(); - /// v.push_back(1); - /// v.clear(); - /// assert!(v.is_empty()); + /// let mut deque = VecDeque::new(); + /// deque.push_back(1); + /// deque.clear(); + /// assert!(deque.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -1315,7 +1319,7 @@ impl VecDeque { self.truncate(0); } - /// Returns `true` if the `VecDeque` contains an element equal to the + /// Returns `true` if the deque contains an element equal to the /// given value. /// /// # Examples @@ -1323,13 +1327,13 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut vector: VecDeque = VecDeque::new(); + /// let mut deque: VecDeque = VecDeque::new(); /// - /// vector.push_back(0); - /// vector.push_back(1); + /// deque.push_back(0); + /// deque.push_back(1); /// - /// assert_eq!(vector.contains(&1), true); - /// assert_eq!(vector.contains(&10), false); + /// assert_eq!(deque.contains(&1), true); + /// assert_eq!(deque.contains(&10), false); /// ``` #[stable(feature = "vec_deque_contains", since = "1.12.0")] pub fn contains(&self, x: &T) -> bool @@ -1340,7 +1344,7 @@ impl VecDeque { a.contains(x) || b.contains(x) } - /// Provides a reference to the front element, or `None` if the `VecDeque` is + /// Provides a reference to the front element, or `None` if the deque is /// empty. /// /// # Examples @@ -1361,7 +1365,7 @@ impl VecDeque { } /// Provides a mutable reference to the front element, or `None` if the - /// `VecDeque` is empty. + /// deque is empty. /// /// # Examples /// @@ -1384,7 +1388,7 @@ impl VecDeque { self.get_mut(0) } - /// Provides a reference to the back element, or `None` if the `VecDeque` is + /// Provides a reference to the back element, or `None` if the deque is /// empty. /// /// # Examples @@ -1405,7 +1409,7 @@ impl VecDeque { } /// Provides a mutable reference to the back element, or `None` if the - /// `VecDeque` is empty. + /// deque is empty. /// /// # Examples /// @@ -1428,7 +1432,7 @@ impl VecDeque { self.get_mut(self.len().wrapping_sub(1)) } - /// Removes the first element and returns it, or `None` if the `VecDeque` is + /// Removes the first element and returns it, or `None` if the deque is /// empty. /// /// # Examples @@ -1455,7 +1459,7 @@ impl VecDeque { } } - /// Removes the last element from the `VecDeque` and returns it, or `None` if + /// Removes the last element from the deque and returns it, or `None` if /// it is empty. /// /// # Examples @@ -1480,7 +1484,7 @@ impl VecDeque { } } - /// Prepends an element to the `VecDeque`. + /// Prepends an element to the deque. /// /// # Examples /// @@ -1505,7 +1509,7 @@ impl VecDeque { } } - /// Appends an element to the back of the `VecDeque`. + /// Appends an element to the back of the deque. /// /// # Examples /// @@ -1535,7 +1539,7 @@ impl VecDeque { self.tail <= self.head } - /// Removes an element from anywhere in the `VecDeque` and returns it, + /// Removes an element from anywhere in the deque and returns it, /// replacing it with the first element. /// /// This does not preserve ordering, but is *O*(1). @@ -1570,8 +1574,8 @@ impl VecDeque { self.pop_front() } - /// Removes an element from anywhere in the `VecDeque` and returns it, replacing it with the - /// last element. + /// Removes an element from anywhere in the deque and returns it, + /// replacing it with the last element. /// /// This does not preserve ordering, but is *O*(1). /// @@ -1605,14 +1609,14 @@ impl VecDeque { self.pop_back() } - /// Inserts an element at `index` within the `VecDeque`, shifting all elements with indices - /// greater than or equal to `index` towards the back. + /// Inserts an element at `index` within the deque, shifting all elements + /// with indices greater than or equal to `index` towards the back. /// /// Element at index 0 is the front of the queue. /// /// # Panics /// - /// Panics if `index` is greater than `VecDeque`'s length + /// Panics if `index` is greater than deque's length /// /// # Examples /// @@ -1829,7 +1833,7 @@ impl VecDeque { } } - /// Removes and returns the element at `index` from the `VecDeque`. + /// Removes and returns the element at `index` from the deque. /// Whichever end is closer to the removal point will be moved to make /// room, and all the affected elements will be moved to new positions. /// Returns `None` if `index` is out of bounds. @@ -2007,10 +2011,10 @@ impl VecDeque { elem } - /// Splits the `VecDeque` into two at the given index. + /// Splits the deque into two at the given index. /// /// Returns a newly allocated `VecDeque`. `self` contains elements `[0, at)`, - /// and the returned `VecDeque` contains elements `[at, len)`. + /// and the returned deque contains elements `[at, len)`. /// /// Note that the capacity of `self` does not change. /// @@ -2025,7 +2029,7 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); + /// let mut buf: VecDeque<_> = [1, 2, 3].into(); /// let buf2 = buf.split_off(1); /// assert_eq!(buf, [1]); /// assert_eq!(buf2, [2, 3]); @@ -2091,8 +2095,8 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque<_> = vec![1, 2].into_iter().collect(); - /// let mut buf2: VecDeque<_> = vec![3, 4].into_iter().collect(); + /// let mut buf: VecDeque<_> = [1, 2].into(); + /// let mut buf2: VecDeque<_> = [3, 4].into(); /// buf.append(&mut buf2); /// assert_eq!(buf, [1, 2, 3, 4]); /// assert_eq!(buf2, []); @@ -2227,7 +2231,7 @@ impl VecDeque { debug_assert!(!self.is_full()); } - /// Modifies the `VecDeque` in-place so that `len()` is equal to `new_len`, + /// Modifies the deque in-place so that `len()` is equal to `new_len`, /// either by removing excess elements from the back or by appending /// elements generated by calling `generator` to the back. /// @@ -2272,7 +2276,7 @@ impl VecDeque { /// /// Once the internal storage is contiguous, the [`as_slices`] and /// [`as_mut_slices`] methods will return the entire contents of the - /// `VecDeque` in a single slice. + /// deque in a single slice. /// /// [`as_slices`]: VecDeque::as_slices /// [`as_mut_slices`]: VecDeque::as_mut_slices @@ -2524,7 +2528,7 @@ impl VecDeque { } } - /// Binary searches this sorted `VecDeque` for a given element. + /// Binary searches the sorted deque for a given element. /// /// If the value is found then [`Result::Ok`] is returned, containing the /// index of the matching element. If there are multiple matches, then any @@ -2547,7 +2551,7 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let deque: VecDeque<_> = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); + /// let deque: VecDeque<_> = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); /// /// assert_eq!(deque.binary_search(&13), Ok(9)); /// assert_eq!(deque.binary_search(&4), Err(7)); @@ -2556,13 +2560,13 @@ impl VecDeque { /// assert!(matches!(r, Ok(1..=4))); /// ``` /// - /// If you want to insert an item to a sorted `VecDeque`, while maintaining + /// If you want to insert an item to a sorted deque, while maintaining /// sort order: /// /// ``` /// use std::collections::VecDeque; /// - /// let mut deque: VecDeque<_> = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); + /// let mut deque: VecDeque<_> = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); /// let num = 42; /// let idx = deque.binary_search(&num).unwrap_or_else(|x| x); /// deque.insert(idx, num); @@ -2577,12 +2581,12 @@ impl VecDeque { self.binary_search_by(|e| e.cmp(x)) } - /// Binary searches this sorted `VecDeque` with a comparator function. + /// Binary searches the sorted deque with a comparator function. /// /// The comparator function should implement an order consistent - /// with the sort order of the underlying `VecDeque`, returning an - /// order code that indicates whether its argument is `Less`, - /// `Equal` or `Greater` than the desired target. + /// with the sort order of the deque, returning an order code that + /// indicates whether its argument is `Less`, `Equal` or `Greater` + /// than the desired target. /// /// If the value is found then [`Result::Ok`] is returned, containing the /// index of the matching element. If there are multiple matches, then any @@ -2605,7 +2609,7 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let deque: VecDeque<_> = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); + /// let deque: VecDeque<_> = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); /// /// assert_eq!(deque.binary_search_by(|x| x.cmp(&13)), Ok(9)); /// assert_eq!(deque.binary_search_by(|x| x.cmp(&4)), Err(7)); @@ -2630,9 +2634,9 @@ impl VecDeque { } } - /// Binary searches this sorted `VecDeque` with a key extraction function. + /// Binary searches the sorted deque with a key extraction function. /// - /// Assumes that the `VecDeque` is sorted by the key, for instance with + /// Assumes that the deque is sorted by the key, for instance with /// [`make_contiguous().sort_by_key()`] using the same key extraction function. /// /// If the value is found then [`Result::Ok`] is returned, containing the @@ -2658,7 +2662,7 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let deque: VecDeque<_> = vec![(0, 0), (2, 1), (4, 1), (5, 1), + /// let deque: VecDeque<_> = [(0, 0), (2, 1), (4, 1), (5, 1), /// (3, 1), (1, 2), (2, 3), (4, 5), (5, 8), (3, 13), /// (1, 21), (2, 34), (4, 55)].into(); /// @@ -2687,7 +2691,7 @@ impl VecDeque { /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0 /// (all odd numbers are at the start, all even at the end). /// - /// If this deque is not partitioned, the returned result is unspecified and meaningless, + /// If the deque is not partitioned, the returned result is unspecified and meaningless, /// as this method performs a kind of binary search. /// /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`]. @@ -2701,7 +2705,7 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let deque: VecDeque<_> = vec![1, 2, 3, 3, 5, 6, 7].into(); + /// let deque: VecDeque<_> = [1, 2, 3, 3, 5, 6, 7].into(); /// let i = deque.partition_point(|&x| x < 5); /// /// assert_eq!(i, 4); @@ -2724,7 +2728,7 @@ impl VecDeque { } impl VecDeque { - /// Modifies the `VecDeque` in-place so that `len()` is equal to new_len, + /// Modifies the deque in-place so that `len()` is equal to new_len, /// either by removing excess elements from the back or by appending clones of `value` /// to the back. /// @@ -2878,7 +2882,7 @@ impl IntoIterator for VecDeque { type Item = T; type IntoIter = IntoIter; - /// Consumes the `VecDeque` into a front-to-back iterator yielding elements by + /// Consumes the deque into a front-to-back iterator yielding elements by /// value. fn into_iter(self) -> IntoIter { IntoIter::new(self) @@ -3049,6 +3053,8 @@ impl From> for Vec { #[stable(feature = "std_collections_from_array", since = "1.56.0")] impl From<[T; N]> for VecDeque { + /// Converts a `[T; N]` into a `VecDeque`. + /// /// ``` /// use std::collections::VecDeque; /// diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index b4d16d74db..aeb7554f8e 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -74,7 +74,7 @@ //! identifier '=' expression //! ``` //! -//! For example, the following [`format!`] expressions all use named argument: +//! For example, the following [`format!`] expressions all use named arguments: //! //! ``` //! format!("{argument}", argument = "test"); // => "test" diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7e663fab16..6da32df57e 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -67,17 +67,14 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables), deny(warnings))) )] -#![cfg_attr( - not(bootstrap), - doc(cfg_hide( - not(test), - not(any(test, bootstrap)), - any(not(feature = "miri-test-libstd"), test, doctest), - no_global_oom_handling, - not(no_global_oom_handling), - target_has_atomic = "ptr" - )) -)] +#![doc(cfg_hide( + not(test), + not(any(test, bootstrap)), + any(not(feature = "miri-test-libstd"), test, doctest), + no_global_oom_handling, + not(no_global_oom_handling), + target_has_atomic = "ptr" +))] #![no_std] #![needs_allocator] // @@ -94,7 +91,7 @@ #![feature(array_chunks)] #![feature(array_methods)] #![feature(array_windows)] -#![feature(async_stream)] +#![feature(async_iterator)] #![feature(coerce_unsized)] #![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))] #![feature(const_box)] @@ -115,11 +112,9 @@ #![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] -#![feature(inherent_ascii_escape)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(layout_for_ptr)] -#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![cfg_attr(test, feature(new_uninit))] #![feature(nonnull_slice_from_raw_parts)] @@ -144,7 +139,7 @@ #![feature(associated_type_bounds)] #![feature(box_syntax)] #![feature(cfg_sanitize)] -#![feature(cfg_target_has_atomic)] +#![cfg_attr(bootstrap, feature(cfg_target_has_atomic))] #![feature(const_deref)] #![feature(const_fn_trait_bound)] #![feature(const_mut_refs)] @@ -152,7 +147,6 @@ #![feature(const_precise_live_drops)] #![feature(const_trait_impl)] #![feature(const_try)] -#![cfg_attr(bootstrap, feature(destructuring_assignment))] #![feature(dropck_eyepatch)] #![feature(exclusive_range_pattern)] #![feature(fundamental)] @@ -168,6 +162,7 @@ #![cfg_attr(test, feature(test))] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] +#![feature(c_unwind)] // // Rustdoc features: #![feature(doc_cfg)] diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 189da9f063..d3e9e65c3f 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -37,6 +37,7 @@ #[cfg(not(test))] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "vec_macro"] #[allow_internal_unstable(box_syntax, liballoc_internals)] macro_rules! vec { () => ( diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 3806bc546e..8fa0242ca9 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -108,7 +108,7 @@ impl RawVec { // 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 { + pub(crate) const MIN_NON_ZERO_CAP: usize = if mem::size_of::() == 1 { 8 } else if mem::size_of::() <= 1024 { 4 diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 33bee4324f..3065169e5e 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -374,33 +374,51 @@ impl Rc { } } - /// Constructs a new `Rc` using a weak reference to itself. Attempting - /// 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. + /// Constructs a new `Rc` using a closure `data_fn` that has access to a + /// weak reference to the constructing `Rc`. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to prevent a memory leak. + /// In `data_fn`, initialization of `T` can make use of the weak reference + /// by cloning and storing it inside `T` for use at a later time. + /// + /// Since the new `Rc` is not fully-constructed until `Rc::new_cyclic` + /// returns, calling [`upgrade`] on the weak reference inside `data_fn` will + /// fail and result in a `None` value. + /// + /// # Panics + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak`] is dropped normally. /// /// # Examples /// /// ``` - /// #![feature(arc_new_cyclic)] /// #![allow(dead_code)] /// use std::rc::{Rc, Weak}; /// /// struct Gadget { - /// self_weak: Weak, - /// // ... more fields + /// me: Weak, /// } + /// /// impl Gadget { - /// pub fn new() -> Rc { - /// Rc::new_cyclic(|self_weak| { - /// Gadget { self_weak: self_weak.clone(), /* ... */ } - /// }) + /// /// Construct a reference counted Gadget. + /// fn new() -> Rc { + /// Rc::new_cyclic(|me| Gadget { me: me.clone() }) + /// } + /// + /// /// Return a reference counted pointer to Self. + /// fn me(&self) -> Rc { + /// self.me.upgrade().unwrap() /// } /// } /// ``` + /// [`upgrade`]: Weak::upgrade #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "arc_new_cyclic", issue = "75861")] - pub fn new_cyclic(data_fn: impl FnOnce(&Weak) -> T) -> Rc { + #[stable(feature = "arc_new_cyclic", since = "1.60.0")] + pub fn new_cyclic(data_fn: F) -> Rc + where + F: FnOnce(&Weak) -> T, + { // Construct the inner in the "uninitialized" state with a single // weak reference. let uninit_ptr: NonNull<_> = Box::leak(box RcBox { @@ -451,12 +469,10 @@ impl Rc { /// /// let mut five = Rc::::new_uninit(); /// - /// let five = unsafe { - /// // Deferred initialization: - /// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// // Deferred initialization: + /// Rc::get_mut(&mut five).unwrap().write(5); /// - /// five.assume_init() - /// }; + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5) /// ``` @@ -543,12 +559,10 @@ impl Rc { /// /// let mut five = Rc::::try_new_uninit()?; /// - /// let five = unsafe { - /// // Deferred initialization: - /// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// // Deferred initialization: + /// Rc::get_mut(&mut five).unwrap().write(5); /// - /// five.assume_init() - /// }; + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5); /// # Ok::<(), std::alloc::AllocError>(()) @@ -660,14 +674,13 @@ impl Rc<[T]> { /// /// let mut values = Rc::<[u32]>::new_uninit_slice(3); /// - /// let values = unsafe { - /// // Deferred initialization: - /// Rc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); - /// Rc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); - /// Rc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); + /// // Deferred initialization: + /// let data = Rc::get_mut(&mut values).unwrap(); + /// data[0].write(1); + /// data[1].write(2); + /// data[2].write(3); /// - /// values.assume_init() - /// }; + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` @@ -738,12 +751,10 @@ impl Rc> { /// /// let mut five = Rc::::new_uninit(); /// - /// let five = unsafe { - /// // Deferred initialization: - /// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// // Deferred initialization: + /// Rc::get_mut(&mut five).unwrap().write(5); /// - /// five.assume_init() - /// }; + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5) /// ``` @@ -777,14 +788,13 @@ impl Rc<[mem::MaybeUninit]> { /// /// let mut values = Rc::<[u32]>::new_uninit_slice(3); /// - /// let values = unsafe { - /// // Deferred initialization: - /// Rc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); - /// Rc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); - /// Rc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); + /// // Deferred initialization: + /// let data = Rc::get_mut(&mut values).unwrap(); + /// data[0].write(1); + /// data[1].write(2); + /// data[2].write(3); /// - /// values.assume_init() - /// }; + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` @@ -1193,6 +1203,41 @@ impl Rc { // reference to the allocation. unsafe { &mut this.ptr.as_mut().value } } + + /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the + /// clone. + /// + /// Assuming `rc_t` is of type `Rc`, this function is functionally equivalent to + /// `(*rc_t).clone()`, but will avoid cloning the inner value where possible. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_unwrap_or_clone)] + /// # use std::{ptr, rc::Rc}; + /// let inner = String::from("test"); + /// let ptr = inner.as_ptr(); + /// + /// let rc = Rc::new(inner); + /// let inner = Rc::unwrap_or_clone(rc); + /// // The inner value was not cloned + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// + /// let rc = Rc::new(inner); + /// let rc2 = rc.clone(); + /// let inner = Rc::unwrap_or_clone(rc); + /// // Because there were 2 references, we had to clone the inner value. + /// assert!(!ptr::eq(ptr, inner.as_ptr())); + /// // `rc2` is the last reference, so when we unwrap it we get back + /// // the original `String`. + /// let inner = Rc::unwrap_or_clone(rc2); + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// ``` + #[inline] + #[unstable(feature = "arc_unwrap_or_clone", issue = "93610")] + pub fn unwrap_or_clone(this: Self) -> T { + Rc::try_unwrap(this).unwrap_or_else(|rc| (*rc).clone()) + } } impl Rc { diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 8853577371..f0397d08f9 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -108,7 +108,7 @@ pub use core::slice::ArrayChunks; pub use core::slice::ArrayChunksMut; #[unstable(feature = "array_windows", issue = "75027")] pub use core::slice::ArrayWindows; -#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub use core::slice::EscapeAscii; #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use core::slice::SliceIndex; @@ -375,7 +375,10 @@ impl [T] { /// Sorts the slice with a key extraction function. /// - /// During sorting, the key function is called only once per element. + /// During sorting, the key function is called at most once per element, by using + /// temporary storage to remember the results of key evaluation. + /// The order of calls to the key function is unspecified and may change in future versions + /// of the standard library. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* log(*n*)) /// worst-case, where the key function is *O*(*m*). diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 7c0faf0659..716bb4983a 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1628,17 +1628,24 @@ impl String { self.vec.clear() } - /// Creates a draining iterator that removes the specified range in the `String` - /// and yields the removed `chars`. + /// Removes the specified range from the string in bulk, returning all + /// removed characters as an iterator. /// - /// Note: The element range is removed even if the iterator is not - /// consumed until the end. + /// The returned iterator keeps a mutable borrow on the string to optimize + /// its implementation. /// /// # Panics /// /// Panics if the starting point or end point do not lie on a [`char`] /// boundary, or if they're out of bounds. /// + /// # Leaking + /// + /// If the returned iterator goes out of scope without being dropped (due to + /// [`core::mem::forget`], for example), the string may still contain a copy + /// of any drained characters, or may have lost characters arbitrarily, + /// including characters outside the range. + /// /// # Examples /// /// Basic usage: @@ -1652,7 +1659,7 @@ impl String { /// assert_eq!(t, "α is alpha, "); /// assert_eq!(s, "β is beta"); /// - /// // A full range clears the string + /// // A full range clears the string, like `clear()` does /// s.drain(..); /// assert_eq!(s, ""); /// ``` diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 7c065f37d1..7e7670aad6 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -351,30 +351,51 @@ impl Arc { unsafe { Self::from_inner(Box::leak(x).into()) } } - /// Constructs a new `Arc` using a weak reference to itself. Attempting - /// 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. + /// Constructs a new `Arc` using a closure `data_fn` that has access to + /// a weak reference to the constructing `Arc`. /// - /// # Examples + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to prevent a memory leak. + /// In `data_fn`, initialization of `T` can make use of the weak reference + /// by cloning and storing it inside `T` for use at a later time. + /// + /// Since the new `Arc` is not fully-constructed until + /// `Arc::new_cyclic` returns, calling [`upgrade`] on the weak + /// reference inside `data_fn` will fail and result in a `None` value. + /// + /// # Panics + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak`] is dropped normally. + /// + /// # Example /// ``` - /// #![feature(arc_new_cyclic)] /// #![allow(dead_code)] - /// /// use std::sync::{Arc, Weak}; /// - /// struct Foo { - /// me: Weak, + /// struct Gadget { + /// me: Weak, /// } /// - /// let foo = Arc::new_cyclic(|me| Foo { - /// me: me.clone(), - /// }); + /// impl Gadget { + /// /// Construct a reference counted Gadget. + /// fn new() -> Arc { + /// Arc::new_cyclic(|me| Gadget { me: me.clone() }) + /// } + /// + /// /// Return a reference counted pointer to Self. + /// fn me(&self) -> Arc { + /// self.me.upgrade().unwrap() + /// } + /// } /// ``` + /// [`upgrade`]: Weak::upgrade #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "arc_new_cyclic", issue = "75861")] - pub fn new_cyclic(data_fn: impl FnOnce(&Weak) -> T) -> Arc { + #[stable(feature = "arc_new_cyclic", since = "1.60.0")] + pub fn new_cyclic(data_fn: F) -> Arc + where + F: FnOnce(&Weak) -> T, + { // Construct the inner in the "uninitialized" state with a single // weak reference. let uninit_ptr: NonNull<_> = Box::leak(box ArcInner { @@ -437,12 +458,10 @@ impl Arc { /// /// let mut five = Arc::::new_uninit(); /// - /// let five = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// // Deferred initialization: + /// Arc::get_mut(&mut five).unwrap().write(5); /// - /// five.assume_init() - /// }; + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5) /// ``` @@ -545,12 +564,10 @@ impl Arc { /// /// let mut five = Arc::::try_new_uninit()?; /// - /// let five = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// // Deferred initialization: + /// Arc::get_mut(&mut five).unwrap().write(5); /// - /// five.assume_init() - /// }; + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5); /// # Ok::<(), std::alloc::AllocError>(()) @@ -652,14 +669,13 @@ impl Arc<[T]> { /// /// let mut values = Arc::<[u32]>::new_uninit_slice(3); /// - /// let values = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); - /// Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); - /// Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); + /// // Deferred initialization: + /// let data = Arc::get_mut(&mut values).unwrap(); + /// data[0].write(1); + /// data[1].write(2); + /// data[2].write(3); /// - /// values.assume_init() - /// }; + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` @@ -730,12 +746,10 @@ impl Arc> { /// /// let mut five = Arc::::new_uninit(); /// - /// let five = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// // Deferred initialization: + /// Arc::get_mut(&mut five).unwrap().write(5); /// - /// five.assume_init() - /// }; + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5) /// ``` @@ -770,14 +784,13 @@ impl Arc<[mem::MaybeUninit]> { /// /// let mut values = Arc::<[u32]>::new_uninit_slice(3); /// - /// let values = unsafe { - /// // Deferred initialization: - /// Arc::get_mut_unchecked(&mut values)[0].as_mut_ptr().write(1); - /// Arc::get_mut_unchecked(&mut values)[1].as_mut_ptr().write(2); - /// Arc::get_mut_unchecked(&mut values)[2].as_mut_ptr().write(3); + /// // Deferred initialization: + /// let data = Arc::get_mut(&mut values).unwrap(); + /// data[0].write(1); + /// data[1].write(2); + /// data[2].write(3); /// - /// values.assume_init() - /// }; + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` @@ -1464,6 +1477,41 @@ impl Arc { // either unique to begin with, or became one upon cloning the contents. unsafe { Self::get_mut_unchecked(this) } } + + /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the + /// clone. + /// + /// Assuming `arc_t` is of type `Arc`, this function is functionally equivalent to + /// `(*arc_t).clone()`, but will avoid cloning the inner value where possible. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_unwrap_or_clone)] + /// # use std::{ptr, sync::Arc}; + /// let inner = String::from("test"); + /// let ptr = inner.as_ptr(); + /// + /// let arc = Arc::new(inner); + /// let inner = Arc::unwrap_or_clone(arc); + /// // The inner value was not cloned + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// + /// let arc = Arc::new(inner); + /// let arc2 = arc.clone(); + /// let inner = Arc::unwrap_or_clone(arc); + /// // Because there were 2 references, we had to clone the inner value. + /// assert!(!ptr::eq(ptr, inner.as_ptr())); + /// // `arc2` is the last reference, so when we unwrap it we get back + /// // the original `String`. + /// let inner = Arc::unwrap_or_clone(arc2); + /// assert!(ptr::eq(ptr, inner.as_ptr())); + /// ``` + #[inline] + #[unstable(feature = "arc_unwrap_or_clone", issue = "93610")] + pub fn unwrap_or_clone(this: Self) -> T { + Arc::try_unwrap(this).unwrap_or_else(|arc| (*arc).clone()) + } } impl Arc { diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 18e191f2b5..f985fb7846 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -125,7 +125,7 @@ impl AsRef<[T]> for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for IntoIter {} #[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for IntoIter {} +unsafe impl Sync for IntoIter {} #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for IntoIter { diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 3ad48a1d28..c29aa0fec5 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1372,9 +1372,12 @@ impl Vec { /// /// Note: Because this shifts over the remaining elements, it has a /// worst-case performance of *O*(*n*). If you don't need the order of elements - /// to be preserved, use [`swap_remove`] instead. + /// to be preserved, use [`swap_remove`] instead. If you'd like to remove + /// elements from the beginning of the `Vec`, consider using + /// [`VecDeque::pop_front`] instead. /// /// [`swap_remove`]: Vec::swap_remove + /// [`VecDeque::pop_front`]: crate::collections::VecDeque::pop_front /// /// # Panics /// @@ -1735,6 +1738,11 @@ impl Vec { /// Removes the last element from a vector and returns it, or [`None`] if it /// is empty. /// + /// If you'd like to pop the first element, consider using + /// [`VecDeque::pop_front`] instead. + /// + /// [`VecDeque::pop_front`]: crate::collections::VecDeque::pop_front + /// /// # Examples /// /// ``` @@ -1791,19 +1799,24 @@ impl Vec { self.len += count; } - /// Creates a draining iterator that removes the specified range in the vector - /// and yields the removed items. + /// Removes the specified range from the vector in bulk, returning all + /// removed elements as an iterator. If the iterator is dropped before + /// being fully consumed, it drops the remaining removed elements. /// - /// When the iterator **is** dropped, all elements in the range are removed - /// from the vector, even if the iterator was not fully consumed. If the - /// iterator **is not** dropped (with [`mem::forget`] for example), it is - /// unspecified how many elements are removed. + /// The returned iterator keeps a mutable borrow on the vector to optimize + /// its implementation. /// /// # Panics /// /// Panics if the starting point is greater than the end point or if /// the end point is greater than the length of the vector. /// + /// # Leaking + /// + /// If the returned iterator goes out of scope without being dropped (due to + /// [`mem::forget`], for example), the vector may have lost and leaked + /// elements arbitrarily, including elements outside the range. + /// /// # Examples /// /// ``` @@ -1812,7 +1825,7 @@ impl Vec { /// assert_eq!(v, &[1]); /// assert_eq!(u, &[2, 3]); /// - /// // A full range clears the vector + /// // A full range clears the vector, like `clear()` does /// v.drain(..); /// assert_eq!(v, &[]); /// ``` @@ -2043,8 +2056,6 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(vec_spare_capacity)] - /// /// // Allocate vector big enough for 10 elements. /// let mut v = Vec::with_capacity(10); /// @@ -2061,7 +2072,7 @@ impl Vec { /// /// assert_eq!(&v, &[0, 1, 2]); /// ``` - #[unstable(feature = "vec_spare_capacity", issue = "75017")] + #[stable(feature = "vec_spare_capacity", since = "1.60.0")] #[inline] pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { // Note: @@ -2900,10 +2911,6 @@ impl From<&mut [T]> for Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_from_array", since = "1.44.0")] impl From<[T; N]> for Vec { - #[cfg(not(test))] - fn from(s: [T; N]) -> Vec { - <[T]>::into_vec(box s) - } /// Allocate a `Vec` and move `s`'s items into it. /// /// # Examples @@ -2911,6 +2918,11 @@ impl From<[T; N]> for Vec { /// ``` /// assert_eq!(Vec::from([1, 2, 3]), vec![1, 2, 3]); /// ``` + #[cfg(not(test))] + fn from(s: [T; N]) -> Vec { + <[T]>::into_vec(box s) + } + #[cfg(test)] fn from(s: [T; N]) -> Vec { crate::slice::into_vec(box s) @@ -3004,14 +3016,12 @@ impl TryFrom> for [T; N] { /// # Examples /// /// ``` - /// use std::convert::TryInto; /// assert_eq!(vec![1, 2, 3].try_into(), Ok([1, 2, 3])); /// assert_eq!(>::new().try_into(), Ok([])); /// ``` /// /// If the length doesn't match, the input comes back in `Err`: /// ``` - /// use std::convert::TryInto; /// let r: Result<[i32; 4], _> = (0..10).collect::>().try_into(); /// assert_eq!(r, Err(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); /// ``` @@ -3019,7 +3029,6 @@ impl TryFrom> for [T; N] { /// If you're fine with just getting a prefix of the `Vec`, /// you can call [`.truncate(N)`](Vec::truncate) first. /// ``` - /// use std::convert::TryInto; /// let mut v = String::from("hello world").into_bytes(); /// v.sort(); /// v.truncate(2); diff --git a/library/alloc/src/vec/spec_from_iter_nested.rs b/library/alloc/src/vec/spec_from_iter_nested.rs index 948cf04419..f915ebb86e 100644 --- a/library/alloc/src/vec/spec_from_iter_nested.rs +++ b/library/alloc/src/vec/spec_from_iter_nested.rs @@ -1,5 +1,8 @@ +use core::cmp; use core::iter::TrustedLen; -use core::ptr::{self}; +use core::ptr; + +use crate::raw_vec::RawVec; use super::{SpecExtend, Vec}; @@ -24,8 +27,11 @@ where None => return Vec::new(), Some(element) => { let (lower, _) = iterator.size_hint(); - let mut vector = Vec::with_capacity(lower.saturating_add(1)); + let initial_capacity = + cmp::max(RawVec::::MIN_NON_ZERO_CAP, lower.saturating_add(1)); + let mut vector = Vec::with_capacity(initial_capacity); unsafe { + // SAFETY: We requested capacity at least 1 ptr::write(vector.as_mut_ptr(), element); vector.set_len(1); } diff --git a/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs index 0d7acfed8c..9e5123be98 100644 --- a/library/alloc/tests/boxed.rs +++ b/library/alloc/tests/boxed.rs @@ -160,7 +160,7 @@ fn const_box() { *boxed = 42; assert!(*boxed == 42); - *boxed + *Box::leak(boxed) }; assert!(VALUE == 42); diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index eec24a5c3f..cbb8626523 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -29,15 +29,16 @@ #![feature(binary_heap_as_slice)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] +#![feature(round_char_boundary)] #![feature(slice_group_by)] #![feature(slice_partition_dedup)] -#![feature(vec_spare_capacity)] #![feature(string_remove_matches)] #![feature(const_btree_new)] #![feature(const_default_impls)] #![feature(const_trait_impl)] #![feature(const_str_from_utf8)] #![feature(nonnull_slice_from_raw_parts)] +#![feature(panic_update_hook)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; diff --git a/library/alloc/tests/linked_list.rs b/library/alloc/tests/linked_list.rs index afcb9e03fd..5f5bd9af2f 100644 --- a/library/alloc/tests/linked_list.rs +++ b/library/alloc/tests/linked_list.rs @@ -304,7 +304,7 @@ fn test_show() { let list: LinkedList<_> = (0..10).collect(); assert_eq!(format!("{:?}", list), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"); - let list: LinkedList<_> = vec!["just", "one", "test", "more"].iter().cloned().collect(); + let list: LinkedList<_> = ["just", "one", "test", "more"].into_iter().collect(); assert_eq!(format!("{:?}", list), "[\"just\", \"one\", \"test\", \"more\"]"); } @@ -336,7 +336,7 @@ fn test_extend() { assert_eq!(a.len(), 4); assert!(a.iter().eq(&[1, 2, 3, 4])); - let b: LinkedList<_> = vec![5, 6, 7].into_iter().collect(); + let b: LinkedList<_> = [5, 6, 7].into_iter().collect(); a.extend(b); // specializes to `append` assert_eq!(a.len(), 7); @@ -375,7 +375,7 @@ fn drain_filter_empty() { #[test] fn drain_filter_zst() { - let mut list: LinkedList<_> = vec![(), (), (), (), ()].into_iter().collect(); + let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect(); let initial_len = list.len(); let mut count = 0; @@ -398,7 +398,7 @@ fn drain_filter_zst() { #[test] fn drain_filter_false() { - let mut list: LinkedList<_> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); let initial_len = list.len(); let mut count = 0; @@ -421,7 +421,7 @@ fn drain_filter_false() { #[test] fn drain_filter_true() { - let mut list: LinkedList<_> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); let initial_len = list.len(); let mut count = 0; @@ -447,7 +447,7 @@ fn drain_filter_true() { fn drain_filter_complex() { { // [+xxx++++++xxxxx++++x+x++] - let mut list = vec![ + let mut list = [ 1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, ] @@ -467,11 +467,10 @@ fn drain_filter_complex() { { // [xxx++++++xxxxx++++x+x++] - let mut list = vec![ - 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, - ] - .into_iter() - .collect::>(); + let mut list = + [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39] + .into_iter() + .collect::>(); let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); @@ -487,7 +486,7 @@ fn drain_filter_complex() { { // [xxx++++++xxxxx++++x+x] let mut list = - vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36] + [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36] .into_iter() .collect::>(); @@ -504,7 +503,7 @@ fn drain_filter_complex() { { // [xxxxxxxxxx+++++++++++] - let mut list = vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19] + let mut list = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19] .into_iter() .collect::>(); @@ -518,7 +517,7 @@ fn drain_filter_complex() { { // [+++++++++++xxxxxxxxxx] - let mut list = vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] + let mut list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] .into_iter() .collect::>(); diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index 18ea6a2141..b93d7938bc 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1783,12 +1783,11 @@ thread_local!(static SILENCE_PANIC: Cell = Cell::new(false)); #[test] #[cfg_attr(target_os = "emscripten", ignore)] // no threads fn panic_safe() { - let prev = panic::take_hook(); - panic::set_hook(Box::new(move |info| { + panic::update_hook(move |prev, info| { if !SILENCE_PANIC.with(|s| s.get()) { prev(info); } - })); + }); let mut rng = thread_rng(); diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index e92881b104..6b8be2506b 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -2230,3 +2230,137 @@ fn utf8_chars() { assert!((!from_utf8(&[0xf0, 0xff, 0x10]).is_ok())); assert!((!from_utf8(&[0xf0, 0xff, 0xff, 0x10]).is_ok())); } + +#[test] +fn utf8_char_counts() { + let strs = [("e", 1), ("é", 1), ("€", 1), ("\u{10000}", 1), ("eé€\u{10000}", 4)]; + let mut reps = + [8, 64, 256, 512, 1024].iter().copied().flat_map(|n| n - 8..=n + 8).collect::>(); + if cfg!(not(miri)) { + let big = 1 << 16; + reps.extend(big - 8..=big + 8); + } + let counts = if cfg!(miri) { 0..1 } else { 0..8 }; + let padding = counts.map(|len| " ".repeat(len)).collect::>(); + + for repeat in reps { + for (tmpl_str, tmpl_char_count) in strs { + for pad_start in &padding { + for pad_end in &padding { + // Create a string with padding... + let with_padding = + format!("{}{}{}", pad_start, tmpl_str.repeat(repeat), pad_end); + // ...and then skip past that padding. This should ensure + // that we test several different alignments for both head + // and tail. + let si = pad_start.len(); + let ei = with_padding.len() - pad_end.len(); + let target = &with_padding[si..ei]; + + assert!(!target.starts_with(" ") && !target.ends_with(" ")); + let expected_count = tmpl_char_count * repeat; + assert_eq!( + expected_count, + target.chars().count(), + "wrong count for `{:?}.repeat({})` (padding: `{:?}`)", + tmpl_str, + repeat, + (pad_start.len(), pad_end.len()), + ); + } + } + } + } +} + +#[test] +fn floor_char_boundary() { + fn check_many(s: &str, arg: impl IntoIterator, ret: usize) { + for idx in arg { + assert_eq!( + s.floor_char_boundary(idx), + ret, + "{:?}.floor_char_boundary({:?}) != {:?}", + s, + idx, + ret + ); + } + } + + // edge case + check_many("", [0, 1, isize::MAX as usize, usize::MAX], 0); + + // basic check + check_many("x", [0], 0); + check_many("x", [1, isize::MAX as usize, usize::MAX], 1); + + // 1-byte chars + check_many("jp", [0], 0); + check_many("jp", [1], 1); + check_many("jp", 2..4, 2); + + // 2-byte chars + check_many("ĵƥ", 0..2, 0); + check_many("ĵƥ", 2..4, 2); + check_many("ĵƥ", 4..6, 4); + + // 3-byte chars + check_many("日本", 0..3, 0); + check_many("日本", 3..6, 3); + check_many("日本", 6..8, 6); + + // 4-byte chars + check_many("🇯🇵", 0..4, 0); + check_many("🇯🇵", 4..8, 4); + check_many("🇯🇵", 8..10, 8); +} + +#[test] +fn ceil_char_boundary() { + fn check_many(s: &str, arg: impl IntoIterator, ret: usize) { + for idx in arg { + assert_eq!( + s.ceil_char_boundary(idx), + ret, + "{:?}.ceil_char_boundary({:?}) != {:?}", + s, + idx, + ret + ); + } + } + + // edge case + check_many("", [0], 0); + + // basic check + check_many("x", [0], 0); + check_many("x", [1], 1); + + // 1-byte chars + check_many("jp", [0], 0); + check_many("jp", [1], 1); + check_many("jp", [2], 2); + + // 2-byte chars + check_many("ĵƥ", 0..=0, 0); + check_many("ĵƥ", 1..=2, 2); + check_many("ĵƥ", 3..=4, 4); + + // 3-byte chars + check_many("日本", 0..=0, 0); + check_many("日本", 1..=3, 3); + check_many("日本", 4..=6, 6); + + // 4-byte chars + check_many("🇯🇵", 0..=0, 0); + check_many("🇯🇵", 1..=4, 4); + check_many("🇯🇵", 5..=8, 8); +} + +#[test] +#[should_panic] +fn ceil_char_boundary_above_len_panic() { + let _ = "x".ceil_char_boundary(2); +} diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 7be137131f..893283e5a2 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -489,7 +489,7 @@ fn test_from_iterator() { b.extend(u.chars()); assert_eq!(s, b); - let c: String = vec![t, u].into_iter().collect(); + let c: String = [t, u].into_iter().collect(); assert_eq!(s, c); let mut d = t.to_string(); diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 7731428253..705914b449 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -449,10 +449,10 @@ fn zero_sized_values() { #[test] fn test_partition() { - assert_eq!(vec![].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3])); - assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 0), (vec![], vec![1, 2, 3])); + assert_eq!([].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![])); + assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![])); + assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3])); + assert_eq!([1, 2, 3].into_iter().partition(|x| *x < 0), (vec![], vec![1, 2, 3])); } #[test] @@ -924,7 +924,7 @@ fn test_into_iter_debug() { #[test] fn test_into_iter_count() { - assert_eq!(vec![1, 2, 3].into_iter().count(), 3); + assert_eq!([1, 2, 3].into_iter().count(), 3); } #[test] @@ -933,7 +933,7 @@ fn test_into_iter_clone() { let v: Vec = it.collect(); assert_eq!(&v[..], slice); } - let mut it = vec![1, 2, 3].into_iter(); + let mut it = [1, 2, 3].into_iter(); iter_equal(it.clone(), &[1, 2, 3]); assert_eq!(it.next(), Some(1)); let mut it = it.rev(); @@ -972,7 +972,7 @@ fn test_into_iter_leak() { #[test] fn test_into_iter_advance_by() { - let mut i = vec![1, 2, 3, 4, 5].into_iter(); + let mut i = [1, 2, 3, 4, 5].into_iter(); i.advance_by(0).unwrap(); i.advance_back_by(0).unwrap(); assert_eq!(i.as_slice(), [1, 2, 3, 4, 5]); @@ -1799,7 +1799,7 @@ fn test_stable_pointers() { assert_eq!(*v0, 13); next_then_drop(v.splice(5..8, vec![1])); // replacement is smaller than original range assert_eq!(*v0, 13); - next_then_drop(v.splice(5..6, vec![1; 10].into_iter().filter(|_| true))); // lower bound not exact + next_then_drop(v.splice(5..6, [1; 10].into_iter().filter(|_| true))); // lower bound not exact assert_eq!(*v0, 13); // spare_capacity_mut diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index ddfb4c00c2..18954f094c 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -927,8 +927,8 @@ fn test_as_mut_slices() { #[test] fn test_append() { - let mut a: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); - let mut b: VecDeque<_> = vec![4, 5, 6].into_iter().collect(); + let mut a: VecDeque<_> = [1, 2, 3].into_iter().collect(); + let mut b: VecDeque<_> = [4, 5, 6].into_iter().collect(); // normal append a.append(&mut b); @@ -1209,7 +1209,7 @@ fn test_try_reserve() { { // Same basic idea, but with non-zero len - let mut ten_bytes: VecDeque = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut ten_bytes: VecDeque = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) { panic!("isize::MAX shouldn't trigger an overflow!"); @@ -1240,7 +1240,7 @@ fn test_try_reserve() { { // Same basic idea, but with interesting type size - let mut ten_u32s: VecDeque = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut ten_u32s: VecDeque = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP / 4 - 10).map_err(|e| e.kind()) { @@ -1322,7 +1322,7 @@ fn test_try_reserve_exact() { } { - let mut ten_bytes: VecDeque = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut ten_bytes: VecDeque = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10).map_err(|e| e.kind()) @@ -1355,7 +1355,7 @@ fn test_try_reserve_exact() { } { - let mut ten_u32s: VecDeque = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + let mut ten_u32s: VecDeque = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP / 4 - 10).map_err(|e| e.kind()) diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 6f10b9e434..6a0c6cd961 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -6,7 +6,9 @@ repository = "https://github.com/rust-lang/rust.git" description = "The Rust Core Library" autotests = false autobenches = false -edition = "2018" +# If you update this, be sure to update it in a bunch of other places too! +# As of 2022, it was the ci/pgo.sh script and the core-no-fp-fmt-parse test. +edition = "2021" [lib] test = false diff --git a/library/core/benches/str.rs b/library/core/benches/str.rs index 1527aa0bd6..78865d81fb 100644 --- a/library/core/benches/str.rs +++ b/library/core/benches/str.rs @@ -1,33 +1,10 @@ use std::str; use test::{black_box, Bencher}; -const LOREM_SHORT: &str = "Lorem ipsum"; - -const LOREM: &str = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. -Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. -Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. -Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. -Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. -At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur"; - -const EMOJI: &str = "😀😃😄😁😆😅🤣😂🙂🙃😉😊😇🥰😍🤩😘😗☺😚😙🥲😋😛😜🤪😝🤑🤗🤭🤫🤔🤐🤨😐😑😶😶‍🌫️😏😒🙄😬😮‍💨🤥😌😔😪🤤😴😷🤒🤕🤢🤮🤧🥵🥶🥴😵😵‍💫🤯🤠🥳🥸😎🤓🧐😕😟🙁☹😮😯😲😳🥺😦😧😨😰😥😢😭😱😖😣😞😓😩😫🥱😤😡😠🤬😈👿💀☠💩🤡👹👺👻👽👾🤖😺😸😹😻😼😽🙀😿😾🙈🙉🙊💋💌💘💝💖💗💓💞💕💟❣💔❤️‍🔥❤️‍🩹❤🧡💛💚💙💜🤎🖤🤍💯💢💥💫💦💨🕳💣💬👁️‍🗨️🗨🗯💭💤👋🤚🖐✋🖖👌🤌🤏✌🤞🤟🤘🤙👈👉👆🖕👇☝👍👎✊👊🤛🤜👏🙌👐🤲🤝🙏✍💅🤳💪🦾🦿🦵🦶👂🦻👃🧠🫀🫁🦷🦴👀👁👅👄👶🧒👦👧🧑👱👨🧔🧔‍♂️🧔‍♀️👨‍🦰👨‍🦱👨‍🦳👨‍🦲👩👩‍🦰🧑‍🦰👩‍🦱🧑‍🦱👩‍🦳🧑‍🦳👩‍🦲🧑‍🦲👱‍♀️👱‍♂️🧓👴👵🙍🙍‍♂️🙍‍♀️🙎🙎‍♂️🙎‍♀️🙅🙅‍♂️🙅‍♀️🙆🙆‍♂️🙆‍♀️💁💁‍♂️💁‍♀️🙋🙋‍♂️🙋‍♀️🧏🧏‍♂️🧏‍♀️🙇🙇‍♂️🙇‍♀️🤦🤦‍♂️🤦‍♀️🤷🤷‍♂️🤷‍♀️🧑‍⚕️👨‍⚕️👩‍⚕️🧑‍🎓👨‍🎓👩‍🎓🧑‍🏫👨‍🏫👩‍🏫🧑‍⚖️👨‍⚖️👩‍⚖️🧑‍🌾👨‍🌾👩‍🌾🧑‍🍳👨‍🍳👩‍🍳🧑‍🔧👨‍🔧👩‍🔧🧑‍🏭👨‍🏭👩‍🏭🧑‍💼👨‍💼👩‍💼🧑‍🔬👨‍🔬👩‍🔬🧑‍💻👨‍💻👩‍💻🧑‍🎤👨‍🎤👩‍🎤🧑‍🎨👨‍🎨👩‍🎨🧑‍✈️👨‍✈️👩‍✈️🧑‍🚀👨‍🚀👩‍🚀🧑‍🚒👨‍🚒👩‍🚒👮👮‍♂️👮‍♀️🕵🕵️‍♂️🕵️‍♀️💂💂‍♂️💂‍♀️🥷👷👷‍♂️👷‍♀️🤴👸👳👳‍♂️👳‍♀️👲🧕🤵🤵‍♂️🤵‍♀️👰👰‍♂️👰‍♀️🤰🤱👩‍🍼👨‍🍼🧑‍🍼👼🎅🤶🧑‍🎄🦸🦸‍♂️🦸‍♀️🦹🦹‍♂️🦹‍♀️🧙🧙‍♂️🧙‍♀️🧚🧚‍♂️🧚‍♀️🧛🧛‍♂️🧛‍♀️🧜🧜‍♂️🧜‍♀️🧝🧝‍♂️🧝‍♀️🧞🧞‍♂️🧞‍♀️🧟🧟‍♂️🧟‍♀️💆💆‍♂️💆‍♀️💇💇‍♂️💇‍♀️🚶🚶‍♂️🚶‍♀️🧍🧍‍♂️🧍‍♀️🧎🧎‍♂️🧎‍♀️🧑‍🦯👨‍🦯👩‍🦯🧑‍🦼👨‍🦼👩‍🦼🧑‍🦽👨‍🦽👩‍🦽🏃🏃‍♂️🏃‍♀️💃🕺🕴👯👯‍♂️👯‍♀️🧖🧖‍♂️🧖‍♀️🧗🧗‍♂️🧗‍♀️🤺🏇⛷🏂🏌🏌️‍♂️🏌️‍♀️🏄🏄‍♂️🏄‍♀️🚣🚣‍♂️🚣‍♀️🏊🏊‍♂️🏊‍♀️⛹⛹️‍♂️⛹️‍♀️🏋🏋️‍♂️🏋️‍♀️🚴🚴‍♂️🚴‍♀️🚵🚵‍♂️🚵‍♀️🤸🤸‍♂️🤸‍♀️🤼🤼‍♂️🤼‍♀️🤽🤽‍♂️🤽‍♀️🤾🤾‍♂️🤾‍♀️🤹🤹‍♂️🤹‍♀️🧘🧘‍♂️🧘‍♀️🛀🛌🧑‍🤝‍🧑👭👫👬💏👩‍❤️‍💋‍👨👨‍❤️‍💋‍👨👩‍❤️‍💋‍👩💑👩‍❤️‍👨👨‍❤️‍👨👩‍❤️‍👩👪👨‍👩‍👦👨‍👩‍👧👨‍👩‍👧‍👦👨‍👩‍👦‍👦👨‍👩‍👧‍👧👨‍👨‍👦👨‍👨‍👧👨‍👨‍👧‍👦👨‍👨‍👦‍👦👨‍👨‍👧‍👧👩‍👩‍👦👩‍👩‍👧👩‍👩‍👧‍👦👩‍👩‍👦‍👦👩‍👩‍👧‍👧👨‍👦👨‍👦‍👦👨‍👧👨‍👧‍👦👨‍👧‍👧👩‍👦👩‍👦‍👦👩‍👧👩‍👧‍👦👩‍👧‍👧🗣👤👥🫂👣🦰🦱🦳🦲🐵🐒🦍🦧🐶🐕🦮🐕‍🦺🐩🐺🦊🦝🐱🐈🐈‍⬛🦁🐯🐅🐆🐴🐎🦄🦓🦌🦬🐮🐂🐃🐄🐷🐖🐗🐽🐏🐑🐐🐪🐫🦙🦒🐘🦣🦏🦛🐭🐁🐀🐹🐰🐇🐿🦫🦔🦇🐻🐻‍❄️🐨🐼🦥🦦🦨🦘🦡🐾🦃🐔🐓🐣🐤🐥🐦🐧🕊🦅🦆🦢🦉🦤🪶🦩🦚🦜🐸🐊🐢🦎🐍🐲🐉🦕🦖🐳🐋🐬🦭🐟🐠🐡🦈🐙🐚🐌🦋🐛🐜🐝🪲🐞🦗🪳🕷🕸🦂🦟🪰🪱🦠💐🌸💮🏵🌹🥀🌺🌻🌼🌷🌱🪴🌲🌳🌴🌵🌾🌿☘🍀🍁🍂🍃🍇🍈🍉🍊🍋🍌🍍🥭🍎🍏🍐🍑🍒🍓🫐🥝🍅🫒🥥🥑🍆🥔🥕🌽🌶🫑🥒🥬🥦🧄🧅🍄🥜🌰🍞🥐🥖🫓🥨🥯🥞🧇🧀🍖🍗🥩🥓🍔🍟🍕🌭🥪🌮🌯🫔🥙🧆🥚🍳🥘🍲🫕🥣🥗🍿🧈🧂🥫🍱🍘🍙🍚🍛🍜🍝🍠🍢🍣🍤🍥🥮🍡🥟🥠🥡🦀🦞🦐🦑🦪🍦🍧🍨🍩🍪🎂🍰🧁🥧🍫🍬🍭🍮🍯🍼🥛☕🫖🍵🍶🍾🍷🍸🍹🍺🍻🥂🥃🥤🧋🧃🧉🧊🥢🍽🍴🥄🔪🏺🌍🌎🌏🌐🗺🗾🧭🏔⛰🌋🗻🏕🏖🏜🏝🏞🏟🏛🏗🧱🪨🪵🛖🏘🏚🏠🏡🏢🏣🏤🏥🏦🏨🏩🏪🏫🏬🏭🏯🏰💒🗼🗽⛪🕌🛕🕍⛩🕋⛲⛺🌁🌃🏙🌄🌅🌆🌇🌉♨🎠🎡🎢💈🎪🚂🚃🚄🚅🚆🚇🚈🚉🚊🚝🚞🚋🚌🚍🚎🚐🚑🚒🚓🚔🚕🚖🚗🚘🚙🛻🚚🚛🚜🏎🏍🛵🦽🦼🛺🚲🛴🛹🛼🚏🛣🛤🛢⛽🚨🚥🚦🛑🚧⚓⛵🛶🚤🛳⛴🛥🚢✈🛩🛫🛬🪂💺🚁🚟🚠🚡🛰🚀🛸🛎🧳⌛⏳⌚⏰⏱⏲🕰🕛🕧🕐🕜🕑🕝🕒🕞🕓🕟🕔🕠🕕🕡🕖🕢🕗🕣🕘🕤🕙🕥🕚🕦🌑🌒🌓🌔🌕🌖🌗🌘🌙🌚🌛🌜🌡☀🌝🌞🪐⭐🌟🌠🌌☁⛅⛈🌤🌥🌦🌧🌨🌩🌪🌫🌬🌀🌈🌂☂☔⛱⚡❄☃⛄☄🔥💧🌊🎃🎄🎆🎇🧨✨🎈🎉🎊🎋🎍🎎🎏🎐🎑🧧🎀🎁🎗🎟🎫🎖🏆🏅🥇🥈🥉⚽⚾🥎🏀🏐🏈🏉🎾🥏🎳🏏🏑🏒🥍🏓🏸🥊🥋🥅⛳⛸🎣🤿🎽🎿🛷🥌🎯🪀🪁🎱🔮🪄🧿🎮🕹🎰🎲🧩🧸🪅🪆♠♥♦♣♟🃏🀄🎴🎭🖼🎨🧵🪡🧶🪢👓🕶🥽🥼🦺👔👕👖🧣🧤🧥🧦👗👘🥻🩱🩲🩳👙👚👛👜👝🛍🎒🩴👞👟🥾🥿👠👡🩰👢👑👒🎩🎓🧢🪖⛑📿💄💍💎🔇🔈🔉🔊📢📣📯🔔🔕🎼🎵🎶🎙🎚🎛🎤🎧📻🎷🪗🎸🎹🎺🎻🪕🥁"; - -#[bench] -fn str_char_count_lorem(b: &mut Bencher) { - b.iter(|| black_box(LOREM).chars().count()); -} - -#[bench] -fn str_char_count_lorem_short(b: &mut Bencher) { - b.iter(|| black_box(LOREM_SHORT).chars().count()); -} - -#[bench] -fn str_char_count_emoji(b: &mut Bencher) { - b.iter(|| black_box(EMOJI).chars().count()); -} +mod char_count; +mod corpora; #[bench] fn str_validate_emoji(b: &mut Bencher) { - b.iter(|| str::from_utf8(black_box(EMOJI.as_bytes()))); + b.iter(|| str::from_utf8(black_box(corpora::emoji::LARGE.as_bytes()))); } diff --git a/library/core/benches/str/char_count.rs b/library/core/benches/str/char_count.rs new file mode 100644 index 0000000000..25d9b2e299 --- /dev/null +++ b/library/core/benches/str/char_count.rs @@ -0,0 +1,107 @@ +use super::corpora::*; +use test::{black_box, Bencher}; + +macro_rules! define_benches { + ($( fn $name: ident($arg: ident: &str) $body: block )+) => { + define_benches!(mod en_tiny, en::TINY, $($name $arg $body)+); + define_benches!(mod en_small, en::SMALL, $($name $arg $body)+); + define_benches!(mod en_medium, en::MEDIUM, $($name $arg $body)+); + define_benches!(mod en_large, en::LARGE, $($name $arg $body)+); + define_benches!(mod en_huge, en::HUGE, $($name $arg $body)+); + + define_benches!(mod zh_tiny, zh::TINY, $($name $arg $body)+); + define_benches!(mod zh_small, zh::SMALL, $($name $arg $body)+); + define_benches!(mod zh_medium, zh::MEDIUM, $($name $arg $body)+); + define_benches!(mod zh_large, zh::LARGE, $($name $arg $body)+); + define_benches!(mod zh_huge, zh::HUGE, $($name $arg $body)+); + + define_benches!(mod ru_tiny, ru::TINY, $($name $arg $body)+); + define_benches!(mod ru_small, ru::SMALL, $($name $arg $body)+); + define_benches!(mod ru_medium, ru::MEDIUM, $($name $arg $body)+); + define_benches!(mod ru_large, ru::LARGE, $($name $arg $body)+); + define_benches!(mod ru_huge, ru::HUGE, $($name $arg $body)+); + + define_benches!(mod emoji_tiny, emoji::TINY, $($name $arg $body)+); + define_benches!(mod emoji_small, emoji::SMALL, $($name $arg $body)+); + define_benches!(mod emoji_medium, emoji::MEDIUM, $($name $arg $body)+); + define_benches!(mod emoji_large, emoji::LARGE, $($name $arg $body)+); + define_benches!(mod emoji_huge, emoji::HUGE, $($name $arg $body)+); + }; + (mod $mod_name: ident, $input: expr, $($name: ident $arg: ident $body: block)+) => { + mod $mod_name { + use super::*; + $( + #[bench] + fn $name(bencher: &mut Bencher) { + let input = $input; + bencher.bytes = input.len() as u64; + let mut input_s = input.to_string(); + bencher.iter(|| { + let $arg: &str = &black_box(&mut input_s); + black_box($body) + }) + } + )+ + } + }; +} + +define_benches! { + fn case00_libcore(s: &str) { + libcore(s) + } + + fn case01_filter_count_cont_bytes(s: &str) { + filter_count_cont_bytes(s) + } + + fn case02_iter_increment(s: &str) { + iterator_increment(s) + } + + fn case03_manual_char_len(s: &str) { + manual_char_len(s) + } +} + +fn libcore(s: &str) -> usize { + s.chars().count() +} + +#[inline] +fn utf8_is_cont_byte(byte: u8) -> bool { + (byte as i8) < -64 +} + +fn filter_count_cont_bytes(s: &str) -> usize { + s.as_bytes().iter().filter(|&&byte| !utf8_is_cont_byte(byte)).count() +} + +fn iterator_increment(s: &str) -> usize { + let mut c = 0; + for _ in s.chars() { + c += 1; + } + c +} + +fn manual_char_len(s: &str) -> usize { + let s = s.as_bytes(); + let mut c = 0; + let mut i = 0; + let l = s.len(); + while i < l { + let b = s[i]; + if b < 0x80 { + i += 1; + } else if b < 0xe0 { + i += 2; + } else if b < 0xf0 { + i += 3; + } else { + i += 4; + } + c += 1; + } + c +} diff --git a/library/core/benches/str/corpora.rs b/library/core/benches/str/corpora.rs new file mode 100644 index 0000000000..b4ac625061 --- /dev/null +++ b/library/core/benches/str/corpora.rs @@ -0,0 +1,88 @@ +//! Exposes a number of modules with different kinds of strings. +//! +//! Each module contains `&str` constants named `TINY`, `SMALL`, `MEDIUM`, +//! `LARGE`, and `HUGE`. +//! +//! - The `TINY` string is generally around 8 bytes. +//! - The `SMALL` string is generally around 30-40 bytes. +//! - The `MEDIUM` string is generally around 600-700 bytes. +//! - The `LARGE` string is the `MEDIUM` string repeated 8x, and is around 5kb. +//! - The `HUGE` string is the `LARGE` string repeated 8x (or the `MEDIUM` +//! string repeated 64x), and is around 40kb. +//! +//! Except for `mod emoji` (which is just a bunch of emoji), the strings were +//! pulled from (localizations of) rust-lang.org. + +macro_rules! repeat8 { + ($s:expr) => { + concat!($s, $s, $s, $s, $s, $s, $s, $s) + }; +} + +macro_rules! define_consts { + ($s:literal) => { + pub const MEDIUM: &str = $s; + pub const LARGE: &str = repeat8!($s); + pub const HUGE: &str = repeat8!(repeat8!(repeat8!($s))); + }; +} + +pub mod en { + pub const TINY: &str = "Mary had"; + pub const SMALL: &str = "Mary had a little lamb, Little lamb"; + define_consts! { + "Rust is blazingly fast and memory-efficient: with no runtime or garbage + collector, it can power performance-critical services, run on embedded + devices, and easily integrate with other languages. Rust’s rich type system + and ownership model guarantee memory-safety and thread-safety — enabling you + to eliminate many classes of bugs at compile-time. Rust has great + documentation, a friendly compiler with useful error messages, and top-notch + tooling — an integrated package manager and build tool, smart multi-editor + support with auto-completion and type inspections, an auto-formatter, and + more." + } +} + +pub mod zh { + pub const TINY: &str = "速度惊"; + pub const SMALL: &str = "速度惊人且内存利用率极高"; + define_consts! { + "Rust 速度惊人且内存利用率极高。由于\ + 没有运行时和垃圾回收,它能够胜任对性能要\ + 求特别高的服务,可以在嵌入式设备上运行,\ + 还能轻松和其他语言集成。Rust 丰富的类型\ + 系统和所有权模型保证了内存安全和线程安全,\ + 让您在编译期就能够消除各种各样的错误。\ + Rust 拥有出色的文档、友好的编译器和清晰\ + 的错误提示信息, 还集成了一流的工具——\ + 包管理器和构建工具, 智能地自动补全和类\ + 型检验的多编辑器支持, 以及自动格式化代\ + 码等等。" + } +} + +pub mod ru { + pub const TINY: &str = "Сотни"; + pub const SMALL: &str = "Сотни компаний по"; + define_consts! { + "Сотни компаний по всему миру используют Rust в реальных\ + проектах для быстрых кросс-платформенных решений с\ + ограниченными ресурсами. Такие проекты, как Firefox,\ + Dropbox и Cloudflare, используют Rust. Rust отлично\ + подходит как для стартапов, так и для больших компаний,\ + как для встраиваемых устройств, так и для масштабируемых\ + web-сервисов. Мой самый большой комплимент Rust." + } +} + +pub mod emoji { + pub const TINY: &str = "😀😃"; + pub const SMALL: &str = "😀😃😄😁😆😅🤣😂🙂🙃😉😊😇🥰😍🤩😘"; + define_consts! { + "😀😃😄😁😆😅🤣😂🙂🙃😉😊😇🥰😍🤩😘😗☺😚😙🥲😋😛😜🤪😝🤑🤗🤭🤫🤔🤐🤨😐😑😶😶‍🌫️😏😒\ + 🙄😬😮‍💨🤥😌😔😪🤤😴😷🤒🤕🤢🤮🤧🥵🥶🥴😵😵‍💫🤯��🥳🥸😎🤓🧐😕😟🙁☹😮😯😲😳🥺😦😧😨\ + 😰😥😢😭😱😖😣😞😓😩😫🥱😤😡😠🤬😈👿💀☠💩🤡👹👺👻👽👾🤖😺😸😹😻😼😽🙀😿😾🙈🙉🙊\ + 💋💌💘💝💖💗💓��💕💟❣💔❤️‍🔥❤️‍🩹❤🧡💛💚💙💜🤎🖤🤍💯💢💥💫💦💨🕳💬👁️‍🗨️🗨🗯💭💤👋\ + 🤚🖐✋🖖👌🤌🤏✌" + } +} diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 37292bf8e2..ee79021ed5 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -66,8 +66,6 @@ where /// /// ```rust /// #![feature(array_from_fn)] -/// # // Apparently these doc tests are still on edition2018 -/// # use std::convert::TryInto; /// /// let array: Result<[u8; 5], _> = std::array::try_from_fn(|i| i.try_into()); /// assert_eq!(array, Ok([0, 1, 2, 3, 4])); @@ -514,6 +512,7 @@ impl [T; N] { /// Returns a slice containing the entire array. Equivalent to `&s[..]`. #[stable(feature = "array_as_slice", since = "1.57.0")] + #[rustc_const_stable(feature = "array_as_slice", since = "1.57.0")] pub const fn as_slice(&self) -> &[T] { self } diff --git a/library/core/src/stream/stream.rs b/library/core/src/async_iter/async_iter.rs similarity index 61% rename from library/core/src/stream/stream.rs rename to library/core/src/async_iter/async_iter.rs index 2cfddf9ad0..f29de31171 100644 --- a/library/core/src/stream/stream.rs +++ b/library/core/src/async_iter/async_iter.rs @@ -4,50 +4,50 @@ 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 +/// This is the main async iterator trait. For more about the concept of async iterators /// generally, please see the [module-level documentation]. In particular, you -/// may want to know how to [implement `Stream`][impl]. +/// may want to know how to [implement `AsyncIterator`][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. +/// [impl]: index.html#implementing-async-iterator +#[unstable(feature = "async_iterator", issue = "79024")] +#[must_use = "async iterators do nothing unless polled"] +pub trait AsyncIterator { + /// The type of items yielded by the async iterator. type Item; - /// Attempt to pull out the next value of this stream, registering the + /// Attempt to pull out the next value of this async iterator, registering the /// current task for wakeup if the value is not yet available, and returning - /// `None` if the stream is exhausted. + /// `None` if the async iterator is exhausted. /// /// # Return value /// /// There are several possible return values, each indicating a distinct - /// stream state: + /// async iterator state: /// - /// - `Poll::Pending` means that this stream's next value is not ready + /// - `Poll::Pending` means that this async iterator'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 + /// - `Poll::Ready(Some(val))` means that the async iterator 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::Ready(None)` means that the async iterator has terminated, and /// `poll_next` should not be invoked again. /// /// # Panics /// - /// Once a stream has finished (returned `Ready(None)` from `poll_next`), calling its + /// Once an async iterator 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 + /// problems; the `AsyncIterator` 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. + /// regardless of the async iterator's state. fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; - /// Returns the bounds on the remaining length of the stream. + /// Returns the bounds on the remaining length of the async iterator. /// /// Specifically, `size_hint()` returns a tuple where the first element /// is the lower bound, and the second element is the upper bound. @@ -58,12 +58,12 @@ pub trait Stream { /// /// # 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 + /// It is not enforced that an async iterator implementation yields the declared + /// number of elements. A buggy async iterator 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 + /// reserving space for the elements of the async iterator, 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. @@ -72,15 +72,15 @@ pub trait Stream { /// because otherwise it would be a violation of the trait's protocol. /// /// The default implementation returns (0, [None]) which is correct for any - /// stream. + /// async iterator. #[inline] fn size_hint(&self) -> (usize, Option) { (0, None) } } -#[unstable(feature = "async_stream", issue = "79024")] -impl Stream for &mut S { +#[unstable(feature = "async_iterator", issue = "79024")] +impl AsyncIterator for &mut S { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -92,16 +92,16 @@ impl Stream for &mut S { } } -#[unstable(feature = "async_stream", issue = "79024")] -impl